aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/advancedDetector.cpp43
-rw-r--r--engines/advancedDetector.h3
-rw-r--r--engines/agi/agi.cpp28
-rw-r--r--engines/agi/agi.h1
-rw-r--r--engines/agi/cycle.cpp2
-rw-r--r--engines/agi/detection.cpp3
-rw-r--r--engines/agi/inv.cpp2
-rw-r--r--engines/agi/lzw.cpp2
-rw-r--r--engines/agi/op_cmd.cpp1
-rw-r--r--engines/agi/picture.cpp2
-rw-r--r--engines/agi/predictive.cpp14
-rw-r--r--engines/agi/saveload.cpp7
-rw-r--r--engines/agi/sound.cpp2
-rw-r--r--engines/agi/sound_midi.cpp15
-rw-r--r--engines/agi/sound_pcjr.cpp5
-rw-r--r--engines/agos/agos.cpp12
-rw-r--r--engines/agos/animation.cpp4
-rw-r--r--engines/agos/detection_tables.h21
-rw-r--r--engines/agos/items.cpp6
-rw-r--r--engines/agos/midi.cpp8
-rw-r--r--engines/agos/midi.h2
-rw-r--r--engines/agos/saveload.cpp9
-rw-r--r--engines/agos/sound.cpp2
-rw-r--r--engines/cine/pal.cpp3
-rw-r--r--engines/cine/part.cpp2
-rw-r--r--engines/cruise/menu.cpp5
-rw-r--r--engines/dialogs.cpp5
-rw-r--r--engines/draci/draci.cpp1
-rw-r--r--engines/draci/music.cpp16
-rw-r--r--engines/draci/script.cpp4
-rw-r--r--engines/drascula/animation.cpp7
-rw-r--r--engines/drascula/converse.cpp10
-rw-r--r--engines/drascula/detection.cpp17
-rw-r--r--engines/drascula/interface.cpp11
-rw-r--r--engines/drascula/objects.cpp9
-rw-r--r--engines/drascula/saveload.cpp4
-rw-r--r--engines/engine.cpp2
-rw-r--r--engines/engines.mk20
-rw-r--r--engines/gob/demos/demoplayer.cpp17
-rw-r--r--engines/gob/detection_tables.h346
-rw-r--r--engines/gob/draw.cpp799
-rw-r--r--engines/gob/draw.h61
-rw-r--r--engines/gob/draw_fascin.cpp848
-rw-r--r--engines/gob/draw_playtoons.cpp81
-rw-r--r--engines/gob/draw_v1.cpp61
-rw-r--r--engines/gob/draw_v2.cpp79
-rw-r--r--engines/gob/driver_vga.cpp244
-rw-r--r--engines/gob/driver_vga.h56
-rw-r--r--engines/gob/expression.cpp2
-rw-r--r--engines/gob/game.cpp8
-rw-r--r--engines/gob/global.h2
-rw-r--r--engines/gob/gob.cpp70
-rw-r--r--engines/gob/gob.h26
-rw-r--r--engines/gob/goblin.cpp4
-rw-r--r--engines/gob/hotspots.cpp82
-rw-r--r--engines/gob/hotspots.h2
-rw-r--r--engines/gob/init.cpp3
-rw-r--r--engines/gob/init.h1
-rw-r--r--engines/gob/init_fascin.cpp58
-rw-r--r--engines/gob/init_v3.cpp7
-rw-r--r--engines/gob/inter.cpp14
-rw-r--r--engines/gob/inter.h4
-rw-r--r--engines/gob/inter_bargon.cpp12
-rw-r--r--engines/gob/inter_fascin.cpp102
-rw-r--r--engines/gob/inter_v1.cpp12
-rw-r--r--engines/gob/inter_v4.cpp6
-rw-r--r--engines/gob/inter_v5.cpp4
-rw-r--r--engines/gob/inter_v6.cpp19
-rw-r--r--engines/gob/module.mk3
-rw-r--r--engines/gob/mult.h2
-rw-r--r--engines/gob/mult_v1.cpp6
-rw-r--r--engines/gob/mult_v2.cpp18
-rw-r--r--engines/gob/palanim.cpp2
-rw-r--r--engines/gob/save/savefile.cpp16
-rw-r--r--engines/gob/save/savefile.h6
-rw-r--r--engines/gob/save/savehandler.cpp8
-rw-r--r--engines/gob/save/savehandler.h4
-rw-r--r--engines/gob/scenery.cpp45
-rw-r--r--engines/gob/sound/sound.cpp9
-rw-r--r--engines/gob/surface.cpp584
-rw-r--r--engines/gob/surface.h131
-rw-r--r--engines/gob/util.cpp9
-rw-r--r--engines/gob/video.cpp398
-rw-r--r--engines/gob/video.h133
-rw-r--r--engines/gob/video_v1.cpp19
-rw-r--r--engines/gob/video_v2.cpp19
-rw-r--r--engines/gob/video_v6.cpp157
-rw-r--r--engines/gob/videoplayer.cpp4
-rw-r--r--engines/gob/videoplayer.h2
-rw-r--r--engines/groovie/detection.cpp15
-rw-r--r--engines/groovie/groovie.cpp11
-rw-r--r--engines/groovie/music.cpp4
-rw-r--r--engines/hugo/detection.cpp202
-rw-r--r--engines/hugo/display.cpp444
-rw-r--r--engines/hugo/display.h134
-rw-r--r--engines/hugo/display_v1d.cpp83
-rw-r--r--engines/hugo/display_v1w.cpp83
-rw-r--r--engines/hugo/engine.cpp968
-rw-r--r--engines/hugo/engine.h44
-rw-r--r--engines/hugo/file.cpp676
-rw-r--r--engines/hugo/file.h155
-rw-r--r--engines/hugo/file_v1d.cpp101
-rw-r--r--engines/hugo/file_v1w.cpp90
-rw-r--r--engines/hugo/file_v2d.cpp177
-rw-r--r--engines/hugo/file_v3d.cpp200
-rw-r--r--engines/hugo/game.h872
-rw-r--r--engines/hugo/global.h54
-rw-r--r--engines/hugo/hugo.cpp1531
-rw-r--r--engines/hugo/hugo.h325
-rw-r--r--engines/hugo/intro.cpp46
-rw-r--r--engines/hugo/intro.h120
-rw-r--r--engines/hugo/intro_v1d.cpp172
-rw-r--r--engines/hugo/intro_v1w.cpp61
-rw-r--r--engines/hugo/intro_v2d.cpp77
-rw-r--r--engines/hugo/intro_v2w.cpp57
-rw-r--r--engines/hugo/intro_v3d.cpp109
-rw-r--r--engines/hugo/intro_v3w.cpp94
-rw-r--r--engines/hugo/inventory.cpp231
-rw-r--r--engines/hugo/inventory.h56
-rw-r--r--engines/hugo/module.mk42
-rw-r--r--engines/hugo/mouse.cpp303
-rw-r--r--engines/hugo/mouse.h54
-rw-r--r--engines/hugo/parser.cpp305
-rw-r--r--engines/hugo/parser.h132
-rw-r--r--engines/hugo/parser_v1d.cpp355
-rw-r--r--engines/hugo/parser_v1w.cpp434
-rw-r--r--engines/hugo/parser_v2d.cpp137
-rw-r--r--engines/hugo/parser_v3d.cpp203
-rw-r--r--engines/hugo/route.cpp487
-rw-r--r--engines/hugo/route.h85
-rw-r--r--engines/hugo/schedule.cpp676
-rw-r--r--engines/hugo/schedule.h105
-rw-r--r--engines/hugo/schedule_v1d.cpp52
-rw-r--r--engines/hugo/schedule_v3d.cpp51
-rw-r--r--engines/hugo/sound.cpp331
-rw-r--r--engines/hugo/sound.h65
-rw-r--r--engines/hugo/util.cpp205
-rw-r--r--engines/hugo/util.h66
-rw-r--r--engines/kyra/debugger.cpp2
-rw-r--r--engines/kyra/detection.cpp13
-rw-r--r--engines/kyra/detection_tables.h16
-rw-r--r--engines/kyra/gui.cpp18
-rw-r--r--engines/kyra/kyra_mr.cpp2
-rw-r--r--engines/kyra/kyra_v1.cpp2
-rw-r--r--engines/kyra/kyra_v1.h1
-rw-r--r--engines/kyra/resource.cpp11
-rw-r--r--engines/kyra/resource.h4
-rw-r--r--engines/kyra/resource_intern.cpp259
-rw-r--r--engines/kyra/resource_intern.h44
-rw-r--r--engines/kyra/saveload.cpp7
-rw-r--r--engines/kyra/screen.cpp16
-rw-r--r--engines/kyra/screen_lol.cpp2
-rw-r--r--engines/kyra/screen_v2.cpp8
-rw-r--r--engines/kyra/script_tim.cpp10
-rw-r--r--engines/kyra/sequences_lol.cpp19
-rw-r--r--engines/kyra/sound_adlib.cpp2
-rw-r--r--engines/kyra/sound_digital.cpp5
-rw-r--r--engines/kyra/sound_midi.cpp22
-rw-r--r--engines/kyra/sound_towns.cpp10
-rw-r--r--engines/kyra/sprites.cpp4
-rw-r--r--engines/kyra/staticres.cpp2
-rw-r--r--engines/kyra/text_hof.cpp2
-rw-r--r--engines/kyra/text_lol.cpp8
-rw-r--r--engines/lure/hotspots.cpp16
-rw-r--r--engines/lure/hotspots.h2
-rw-r--r--engines/lure/res.cpp6
-rw-r--r--engines/lure/scripts.cpp15
-rw-r--r--engines/lure/sound.cpp6
-rw-r--r--engines/m4/globals.h65
-rw-r--r--engines/m4/m4.h12
-rw-r--r--engines/m4/mads_logic.cpp767
-rw-r--r--engines/m4/mads_logic.h52
-rw-r--r--engines/m4/mads_menus.cpp2
-rw-r--r--engines/m4/mads_scene.cpp8
-rw-r--r--engines/m4/mads_scene.h5
-rw-r--r--engines/m4/mads_views.cpp13
-rw-r--r--engines/m4/scene.cpp1
-rw-r--r--engines/m4/scene.h7
-rw-r--r--engines/m4/ws_machine.cpp4
-rw-r--r--engines/m4/ws_sequence.cpp2
-rw-r--r--engines/made/made.cpp1
-rw-r--r--engines/made/music.cpp6
-rw-r--r--engines/mohawk/console.cpp41
-rw-r--r--engines/mohawk/console.h1
-rw-r--r--engines/mohawk/dialogs.cpp8
-rw-r--r--engines/mohawk/graphics.cpp153
-rw-r--r--engines/mohawk/graphics.h7
-rw-r--r--engines/mohawk/myst.cpp5
-rw-r--r--engines/mohawk/myst_scripts.cpp9
-rw-r--r--engines/mohawk/riven.cpp306
-rw-r--r--engines/mohawk/riven.h32
-rw-r--r--engines/mohawk/riven_cursors.h296
-rw-r--r--engines/mohawk/riven_external.cpp941
-rw-r--r--engines/mohawk/riven_external.h14
-rw-r--r--engines/mohawk/riven_saveload.cpp8
-rw-r--r--engines/mohawk/riven_scripts.cpp25
-rw-r--r--engines/mohawk/riven_scripts.h6
-rw-r--r--engines/mohawk/riven_vars.cpp55
-rw-r--r--engines/mohawk/sound.cpp54
-rw-r--r--engines/mohawk/sound.h13
-rw-r--r--engines/mohawk/video.cpp18
-rw-r--r--engines/mohawk/video.h2
-rw-r--r--engines/parallaction/balloons.cpp4
-rw-r--r--engines/parallaction/disk_br.cpp2
-rw-r--r--engines/parallaction/gui_ns.cpp2
-rw-r--r--engines/parallaction/parallaction.cpp2
-rw-r--r--engines/parallaction/sound_br.cpp4
-rw-r--r--engines/parallaction/walk.cpp2
-rw-r--r--engines/queen/cutaway.cpp6
-rw-r--r--engines/queen/music.cpp5
-rw-r--r--engines/queen/talk.cpp4
-rw-r--r--engines/saga/actor.h4
-rw-r--r--engines/saga/font.cpp4
-rw-r--r--engines/saga/font.h2
-rw-r--r--engines/saga/interface.cpp76
-rw-r--r--engines/saga/isomap.cpp22
-rw-r--r--engines/saga/music.cpp14
-rw-r--r--engines/saga/music.h2
-rw-r--r--engines/saga/puzzle.cpp6
-rw-r--r--engines/saga/resource.cpp29
-rw-r--r--engines/saga/scene.cpp4
-rw-r--r--engines/saga/script.cpp6
-rw-r--r--engines/saga/sndres.cpp6
-rw-r--r--engines/sci/console.cpp105
-rw-r--r--engines/sci/console.h4
-rw-r--r--engines/sci/debug.h6
-rw-r--r--engines/sci/detection.cpp28
-rw-r--r--engines/sci/detection_tables.h346
-rw-r--r--engines/sci/engine/features.cpp17
-rw-r--r--engines/sci/engine/gc.cpp13
-rw-r--r--engines/sci/engine/kernel.cpp8
-rw-r--r--engines/sci/engine/kernel.h12
-rw-r--r--engines/sci/engine/kernel_tables.h142
-rw-r--r--engines/sci/engine/kevent.cpp33
-rw-r--r--engines/sci/engine/kfile.cpp411
-rw-r--r--engines/sci/engine/kgraphics.cpp374
-rw-r--r--engines/sci/engine/klists.cpp3
-rw-r--r--engines/sci/engine/kmath.cpp66
-rw-r--r--engines/sci/engine/kmenu.cpp12
-rw-r--r--engines/sci/engine/kmisc.cpp16
-rw-r--r--engines/sci/engine/kmovement.cpp495
-rw-r--r--engines/sci/engine/kparse.cpp24
-rw-r--r--engines/sci/engine/kpathing.cpp64
-rw-r--r--engines/sci/engine/kscripts.cpp74
-rw-r--r--engines/sci/engine/kstring.cpp32
-rw-r--r--engines/sci/engine/kvideo.cpp2
-rw-r--r--engines/sci/engine/savegame.cpp58
-rw-r--r--engines/sci/engine/savegame.h2
-rw-r--r--engines/sci/engine/script.cpp29
-rw-r--r--engines/sci/engine/script.h23
-rw-r--r--engines/sci/engine/script_patches.cpp606
-rw-r--r--engines/sci/engine/seg_manager.cpp22
-rw-r--r--engines/sci/engine/segment.h9
-rw-r--r--engines/sci/engine/selector.cpp6
-rw-r--r--engines/sci/engine/selector.h7
-rw-r--r--engines/sci/engine/state.cpp12
-rw-r--r--engines/sci/engine/state.h28
-rw-r--r--engines/sci/engine/static_selectors.cpp128
-rw-r--r--engines/sci/engine/vm.cpp148
-rw-r--r--engines/sci/engine/workarounds.cpp72
-rw-r--r--engines/sci/engine/workarounds.h3
-rw-r--r--engines/sci/event.cpp19
-rw-r--r--engines/sci/graphics/animate.cpp123
-rw-r--r--engines/sci/graphics/animate.h6
-rw-r--r--engines/sci/graphics/compare.cpp26
-rw-r--r--engines/sci/graphics/controls.cpp46
-rw-r--r--engines/sci/graphics/cursor.cpp133
-rw-r--r--engines/sci/graphics/cursor.h23
-rw-r--r--engines/sci/graphics/frameout.cpp131
-rw-r--r--engines/sci/graphics/frameout.h9
-rw-r--r--engines/sci/graphics/menu.cpp2
-rw-r--r--engines/sci/graphics/paint16.cpp8
-rw-r--r--engines/sci/graphics/palette.cpp25
-rw-r--r--engines/sci/graphics/picture.cpp17
-rw-r--r--engines/sci/graphics/portrait.cpp2
-rw-r--r--engines/sci/graphics/ports.cpp29
-rw-r--r--engines/sci/graphics/robot.cpp169
-rw-r--r--engines/sci/graphics/robot.h1
-rw-r--r--engines/sci/graphics/screen.cpp6
-rw-r--r--engines/sci/graphics/text16.cpp79
-rw-r--r--engines/sci/graphics/text16.h10
-rw-r--r--engines/sci/graphics/transitions.cpp144
-rw-r--r--engines/sci/graphics/transitions.h5
-rw-r--r--engines/sci/graphics/view.cpp7
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/parser/grammar.cpp88
-rw-r--r--engines/sci/parser/said.cpp40
-rw-r--r--engines/sci/parser/vocabulary.cpp236
-rw-r--r--engines/sci/parser/vocabulary.h53
-rw-r--r--engines/sci/resource.cpp119
-rw-r--r--engines/sci/resource.h5
-rw-r--r--engines/sci/resource_audio.cpp46
-rw-r--r--engines/sci/sci.cpp247
-rw-r--r--engines/sci/sci.h22
-rw-r--r--engines/sci/sound/drivers/adlib.cpp10
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp4
-rw-r--r--engines/sci/sound/drivers/cms.cpp817
-rw-r--r--engines/sci/sound/drivers/fb01.cpp4
-rw-r--r--engines/sci/sound/drivers/map-mt32-to-gm.h170
-rw-r--r--engines/sci/sound/drivers/midi.cpp58
-rw-r--r--engines/sci/sound/drivers/mididriver.h25
-rw-r--r--engines/sci/sound/drivers/pcjr.cpp8
-rw-r--r--engines/sci/sound/midiparser_sci.cpp43
-rw-r--r--engines/sci/sound/midiparser_sci.h4
-rw-r--r--engines/sci/sound/music.cpp100
-rw-r--r--engines/sci/sound/music.h7
-rw-r--r--engines/sci/sound/soundcmd.cpp21
-rw-r--r--engines/sci/sound/soundcmd.h7
-rw-r--r--engines/scumm/actor.cpp7
-rw-r--r--engines/scumm/charset.cpp161
-rw-r--r--engines/scumm/charset.h16
-rw-r--r--engines/scumm/cursor.cpp33
-rw-r--r--engines/scumm/detection.cpp4
-rw-r--r--engines/scumm/detection_tables.h16
-rw-r--r--engines/scumm/dialogs.cpp17
-rw-r--r--engines/scumm/dialogs.h3
-rw-r--r--engines/scumm/gfx.cpp283
-rw-r--r--engines/scumm/gfx.h63
-rw-r--r--engines/scumm/gfx_towns.cpp522
-rw-r--r--engines/scumm/he/wiz_he.cpp2
-rw-r--r--engines/scumm/help.cpp2
-rw-r--r--engines/scumm/imuse/imuse_player.cpp4
-rw-r--r--engines/scumm/midiparser_eup.cpp222
-rw-r--r--engines/scumm/module.mk3
-rw-r--r--engines/scumm/object.cpp2
-rw-r--r--engines/scumm/palette.cpp73
-rw-r--r--engines/scumm/player_sid.cpp24
-rw-r--r--engines/scumm/player_towns.cpp748
-rw-r--r--engines/scumm/player_towns.h181
-rw-r--r--engines/scumm/player_v2cms.cpp385
-rw-r--r--engines/scumm/room.cpp5
-rw-r--r--engines/scumm/saveload.cpp51
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script.cpp19
-rw-r--r--engines/scumm/script_v2.cpp1
-rw-r--r--engines/scumm/script_v3.cpp40
-rw-r--r--engines/scumm/script_v4.cpp64
-rw-r--r--engines/scumm/script_v5.cpp119
-rw-r--r--engines/scumm/scumm-md5.h12
-rw-r--r--engines/scumm/scumm.cpp158
-rw-r--r--engines/scumm/scumm.h51
-rw-r--r--engines/scumm/scumm_v0.h5
-rw-r--r--engines/scumm/scumm_v2.h2
-rw-r--r--engines/scumm/scumm_v3.h2
-rw-r--r--engines/scumm/sound.cpp101
-rw-r--r--engines/scumm/sound.h1
-rw-r--r--engines/scumm/string.cpp17
-rw-r--r--engines/scumm/vars.cpp6
-rw-r--r--engines/scumm/verbs.cpp72
-rw-r--r--engines/sky/music/gmmusic.cpp1
-rw-r--r--engines/sky/music/mt32music.cpp1
-rw-r--r--engines/sky/sky.cpp2
-rw-r--r--engines/sword1/logic.cpp4
-rw-r--r--engines/sword1/router.cpp20
-rw-r--r--engines/sword1/screen.cpp6
-rw-r--r--engines/sword1/sound.cpp2
-rw-r--r--engines/sword1/sword1.cpp18
-rw-r--r--engines/sword1/text.cpp6
-rw-r--r--engines/sword2/mouse.cpp2
-rw-r--r--engines/sword2/screen.cpp4
-rw-r--r--engines/sword2/sprite.cpp10
-rw-r--r--engines/sword2/sword2.cpp20
-rw-r--r--engines/sword25/detection.cpp130
-rw-r--r--engines/sword25/fmv/movieplayer.cpp154
-rw-r--r--engines/sword25/fmv/movieplayer.h145
-rw-r--r--engines/sword25/fmv/movieplayer_script.cpp163
-rw-r--r--engines/sword25/fmv/theora_decoder.cpp492
-rw-r--r--engines/sword25/fmv/theora_decoder.h152
-rw-r--r--engines/sword25/fmv/yuvtorgba.cpp243
-rw-r--r--engines/sword25/fmv/yuvtorgba.h51
-rw-r--r--engines/sword25/gfx/animation.cpp711
-rw-r--r--engines/sword25/gfx/animation.h227
-rw-r--r--engines/sword25/gfx/animationdescription.cpp65
-rw-r--r--engines/sword25/gfx/animationdescription.h103
-rw-r--r--engines/sword25/gfx/animationresource.cpp254
-rw-r--r--engines/sword25/gfx/animationresource.h123
-rw-r--r--engines/sword25/gfx/animationtemplate.cpp243
-rw-r--r--engines/sword25/gfx/animationtemplate.h125
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.cpp107
-rw-r--r--engines/sword25/gfx/animationtemplateregistry.h68
-rw-r--r--engines/sword25/gfx/bitmap.cpp215
-rw-r--r--engines/sword25/gfx/bitmap.h197
-rw-r--r--engines/sword25/gfx/bitmapresource.cpp68
-rw-r--r--engines/sword25/gfx/bitmapresource.h213
-rw-r--r--engines/sword25/gfx/dynamicbitmap.cpp193
-rw-r--r--engines/sword25/gfx/dynamicbitmap.h87
-rw-r--r--engines/sword25/gfx/fontresource.cpp153
-rw-r--r--engines/sword25/gfx/fontresource.h154
-rw-r--r--engines/sword25/gfx/framecounter.cpp68
-rw-r--r--engines/sword25/gfx/framecounter.h94
-rw-r--r--engines/sword25/gfx/graphicengine.cpp512
-rw-r--r--engines/sword25/gfx/graphicengine.h398
-rw-r--r--engines/sword25/gfx/graphicengine_script.cpp1553
-rw-r--r--engines/sword25/gfx/image/art.cpp2651
-rw-r--r--engines/sword25/gfx/image/art.h279
-rw-r--r--engines/sword25/gfx/image/b25sloader.cpp119
-rw-r--r--engines/sword25/gfx/image/b25sloader.h67
-rw-r--r--engines/sword25/gfx/image/image.h222
-rw-r--r--engines/sword25/gfx/image/imageloader.cpp129
-rw-r--r--engines/sword25/gfx/image/imageloader.h209
-rw-r--r--engines/sword25/gfx/image/imageloader_ids.h62
-rw-r--r--engines/sword25/gfx/image/pngloader.cpp283
-rw-r--r--engines/sword25/gfx/image/pngloader.h79
-rw-r--r--engines/sword25/gfx/image/renderedimage.cpp398
-rw-r--r--engines/sword25/gfx/image/renderedimage.h121
-rw-r--r--engines/sword25/gfx/image/swimage.cpp134
-rw-r--r--engines/sword25/gfx/image/swimage.h107
-rw-r--r--engines/sword25/gfx/image/vectorimage.cpp637
-rw-r--r--engines/sword25/gfx/image/vectorimage.h237
-rw-r--r--engines/sword25/gfx/image/vectorimagerenderer.cpp461
-rw-r--r--engines/sword25/gfx/panel.cpp135
-rw-r--r--engines/sword25/gfx/panel.h81
-rw-r--r--engines/sword25/gfx/renderobject.cpp560
-rw-r--r--engines/sword25/gfx/renderobject.h530
-rw-r--r--engines/sword25/gfx/renderobjectmanager.cpp151
-rw-r--r--engines/sword25/gfx/renderobjectmanager.h131
-rw-r--r--engines/sword25/gfx/renderobjectptr.h79
-rw-r--r--engines/sword25/gfx/renderobjectregistry.cpp53
-rw-r--r--engines/sword25/gfx/renderobjectregistry.h78
-rw-r--r--engines/sword25/gfx/rootrenderobject.h72
-rw-r--r--engines/sword25/gfx/screenshot.cpp189
-rw-r--r--engines/sword25/gfx/screenshot.h59
-rw-r--r--engines/sword25/gfx/staticbitmap.cpp221
-rw-r--r--engines/sword25/gfx/staticbitmap.h89
-rw-r--r--engines/sword25/gfx/text.cpp384
-rw-r--r--engines/sword25/gfx/text.h181
-rw-r--r--engines/sword25/gfx/timedrenderobject.cpp52
-rw-r--r--engines/sword25/gfx/timedrenderobject.h67
-rw-r--r--engines/sword25/input/inputengine.cpp399
-rw-r--r--engines/sword25/input/inputengine.h333
-rw-r--r--engines/sword25/input/inputengine_script.cpp355
-rw-r--r--engines/sword25/kernel/bs_stdint.h54
-rw-r--r--engines/sword25/kernel/callbackregistry.cpp131
-rw-r--r--engines/sword25/kernel/callbackregistry.h93
-rw-r--r--engines/sword25/kernel/common.h60
-rw-r--r--engines/sword25/kernel/filesystemutil.cpp152
-rw-r--r--engines/sword25/kernel/filesystemutil.h114
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.cpp182
-rw-r--r--engines/sword25/kernel/inputpersistenceblock.h90
-rw-r--r--engines/sword25/kernel/kernel.cpp454
-rw-r--r--engines/sword25/kernel/kernel.h370
-rw-r--r--engines/sword25/kernel/kernel_script.cpp739
-rw-r--r--engines/sword25/kernel/log.cpp214
-rw-r--r--engines/sword25/kernel/log.h147
-rw-r--r--engines/sword25/kernel/objectregistry.h175
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.cpp131
-rw-r--r--engines/sword25/kernel/outputpersistenceblock.h78
-rw-r--r--engines/sword25/kernel/persistable.h53
-rw-r--r--engines/sword25/kernel/persistenceblock.h133
-rw-r--r--engines/sword25/kernel/persistenceservice.cpp465
-rw-r--r--engines/sword25/kernel/persistenceservice.h84
-rw-r--r--engines/sword25/kernel/resmanager.cpp336
-rw-r--r--engines/sword25/kernel/resmanager.h193
-rw-r--r--engines/sword25/kernel/resource.cpp60
-rw-r--r--engines/sword25/kernel/resource.h118
-rw-r--r--engines/sword25/kernel/resservice.h75
-rw-r--r--engines/sword25/kernel/scummvmwindow.cpp297
-rw-r--r--engines/sword25/kernel/scummvmwindow.h85
-rw-r--r--engines/sword25/kernel/service.h75
-rw-r--r--engines/sword25/kernel/service_ids.h80
-rw-r--r--engines/sword25/kernel/string.h111
-rw-r--r--engines/sword25/kernel/window.cpp69
-rw-r--r--engines/sword25/kernel/window.h177
-rw-r--r--engines/sword25/math/geometry.cpp53
-rw-r--r--engines/sword25/math/geometry.h56
-rw-r--r--engines/sword25/math/geometry_script.cpp496
-rw-r--r--engines/sword25/math/line.h198
-rw-r--r--engines/sword25/math/polygon.cpp491
-rw-r--r--engines/sword25/math/polygon.h262
-rw-r--r--engines/sword25/math/region.cpp354
-rw-r--r--engines/sword25/math/region.h241
-rw-r--r--engines/sword25/math/regionregistry.cpp106
-rw-r--r--engines/sword25/math/regionregistry.h66
-rw-r--r--engines/sword25/math/vertex.cpp93
-rw-r--r--engines/sword25/math/vertex.h180
-rw-r--r--engines/sword25/math/walkregion.cpp390
-rw-r--r--engines/sword25/math/walkregion.h113
-rw-r--r--engines/sword25/module.mk109
-rw-r--r--engines/sword25/package/packagemanager.cpp293
-rw-r--r--engines/sword25/package/packagemanager.h222
-rw-r--r--engines/sword25/package/packagemanager_script.cpp215
-rw-r--r--engines/sword25/script/lua_extensions.cpp75
-rw-r--r--engines/sword25/script/luabindhelper.cpp427
-rw-r--r--engines/sword25/script/luabindhelper.h128
-rw-r--r--engines/sword25/script/luacallback.cpp180
-rw-r--r--engines/sword25/script/luacallback.h78
-rw-r--r--engines/sword25/script/luascript.cpp569
-rw-r--r--engines/sword25/script/luascript.h116
-rw-r--r--engines/sword25/script/script.h96
-rw-r--r--engines/sword25/sfx/soundengine.cpp274
-rw-r--r--engines/sword25/sfx/soundengine.h263
-rw-r--r--engines/sword25/sfx/soundengine_script.cpp365
-rw-r--r--engines/sword25/sword25.cpp189
-rw-r--r--engines/sword25/sword25.h80
-rw-r--r--engines/sword25/util/lua/COPYRIGHT34
-rw-r--r--engines/sword25/util/lua/HISTORY183
-rw-r--r--engines/sword25/util/lua/README37
-rw-r--r--engines/sword25/util/lua/lapi.c1085
-rw-r--r--engines/sword25/util/lua/lapi.h16
-rw-r--r--engines/sword25/util/lua/lauxlib.c652
-rw-r--r--engines/sword25/util/lua/lauxlib.h174
-rw-r--r--engines/sword25/util/lua/lbaselib.c654
-rw-r--r--engines/sword25/util/lua/lcode.c839
-rw-r--r--engines/sword25/util/lua/lcode.h76
-rw-r--r--engines/sword25/util/lua/ldblib.c397
-rw-r--r--engines/sword25/util/lua/ldebug.c622
-rw-r--r--engines/sword25/util/lua/ldebug.h33
-rw-r--r--engines/sword25/util/lua/ldo.c518
-rw-r--r--engines/sword25/util/lua/ldo.h57
-rw-r--r--engines/sword25/util/lua/ldump.c164
-rw-r--r--engines/sword25/util/lua/lfunc.c174
-rw-r--r--engines/sword25/util/lua/lfunc.h34
-rw-r--r--engines/sword25/util/lua/lgc.c711
-rw-r--r--engines/sword25/util/lua/lgc.h110
-rw-r--r--engines/sword25/util/lua/linit.c38
-rw-r--r--engines/sword25/util/lua/liolib.c553
-rw-r--r--engines/sword25/util/lua/llex.c461
-rw-r--r--engines/sword25/util/lua/llex.h81
-rw-r--r--engines/sword25/util/lua/llimits.h128
-rw-r--r--engines/sword25/util/lua/lmathlib.c263
-rw-r--r--engines/sword25/util/lua/lmem.c86
-rw-r--r--engines/sword25/util/lua/lmem.h49
-rw-r--r--engines/sword25/util/lua/loadlib.c664
-rw-r--r--engines/sword25/util/lua/lobject.c214
-rw-r--r--engines/sword25/util/lua/lobject.h381
-rw-r--r--engines/sword25/util/lua/lopcodes.c102
-rw-r--r--engines/sword25/util/lua/lopcodes.h268
-rw-r--r--engines/sword25/util/lua/loslib.c243
-rw-r--r--engines/sword25/util/lua/lparser.c1339
-rw-r--r--engines/sword25/util/lua/lparser.h82
-rw-r--r--engines/sword25/util/lua/lstate.c214
-rw-r--r--engines/sword25/util/lua/lstate.h169
-rw-r--r--engines/sword25/util/lua/lstring.c111
-rw-r--r--engines/sword25/util/lua/lstring.h31
-rw-r--r--engines/sword25/util/lua/lstrlib.c868
-rw-r--r--engines/sword25/util/lua/ltable.c588
-rw-r--r--engines/sword25/util/lua/ltable.h40
-rw-r--r--engines/sword25/util/lua/ltablib.c279
-rw-r--r--engines/sword25/util/lua/ltm.c75
-rw-r--r--engines/sword25/util/lua/ltm.h54
-rw-r--r--engines/sword25/util/lua/lua.c392
-rw-r--r--engines/sword25/util/lua/lua.h388
-rw-r--r--engines/sword25/util/lua/luac.c200
-rw-r--r--engines/sword25/util/lua/luaconf.h763
-rw-r--r--engines/sword25/util/lua/lualib.h53
-rw-r--r--engines/sword25/util/lua/lundump.c225
-rw-r--r--engines/sword25/util/lua/lundump.h36
-rw-r--r--engines/sword25/util/lua/lvm.c763
-rw-r--r--engines/sword25/util/lua/lvm.h36
-rw-r--r--engines/sword25/util/lua/lzio.c82
-rw-r--r--engines/sword25/util/lua/lzio.h67
-rw-r--r--engines/sword25/util/lua/print.c227
-rw-r--r--engines/sword25/util/pluto/CHANGELOG38
-rw-r--r--engines/sword25/util/pluto/FILEFORMAT168
-rw-r--r--engines/sword25/util/pluto/Makefile29
-rw-r--r--engines/sword25/util/pluto/README133
-rw-r--r--engines/sword25/util/pluto/THANKS10
-rw-r--r--engines/sword25/util/pluto/pdep.c112
-rw-r--r--engines/sword25/util/pluto/pdep/README5
-rw-r--r--engines/sword25/util/pluto/pdep/lauxlib.h174
-rw-r--r--engines/sword25/util/pluto/pdep/ldo.h57
-rw-r--r--engines/sword25/util/pluto/pdep/lfunc.h34
-rw-r--r--engines/sword25/util/pluto/pdep/lgc.h110
-rw-r--r--engines/sword25/util/pluto/pdep/llimits.h128
-rw-r--r--engines/sword25/util/pluto/pdep/lobject.h381
-rw-r--r--engines/sword25/util/pluto/pdep/lopcodes.h268
-rw-r--r--engines/sword25/util/pluto/pdep/lstate.h169
-rw-r--r--engines/sword25/util/pluto/pdep/lstring.h31
-rw-r--r--engines/sword25/util/pluto/pdep/ltm.h54
-rw-r--r--engines/sword25/util/pluto/pdep/lua.h388
-rw-r--r--engines/sword25/util/pluto/pdep/lzio.h65
-rw-r--r--engines/sword25/util/pluto/pdep/pdep.h41
-rw-r--r--engines/sword25/util/pluto/pluto.c1658
-rw-r--r--engines/sword25/util/pluto/pluto.h25
-rw-r--r--engines/sword25/util/pluto/plzio.c76
-rw-r--r--engines/sword25/util/pluto/pptest.c95
-rw-r--r--engines/sword25/util/pluto/pptest.lua168
-rw-r--r--engines/sword25/util/pluto/puptest.c81
-rw-r--r--engines/sword25/util/pluto/puptest.lua93
-rw-r--r--engines/testbed/config-params.cpp73
-rw-r--r--engines/testbed/config-params.h99
-rw-r--r--engines/testbed/config.cpp308
-rw-r--r--engines/testbed/config.h135
-rw-r--r--engines/testbed/detection.cpp92
-rw-r--r--engines/testbed/events.cpp314
-rw-r--r--engines/testbed/events.h67
-rw-r--r--engines/testbed/fs.cpp198
-rw-r--r--engines/testbed/fs.h74
-rw-r--r--engines/testbed/graphics.cpp1150
-rw-r--r--engines/testbed/graphics.h94
-rw-r--r--engines/testbed/midi.cpp155
-rw-r--r--engines/testbed/midi.h77
-rw-r--r--engines/testbed/misc.cpp172
-rw-r--r--engines/testbed/misc.h80
-rw-r--r--engines/testbed/module.mk26
-rw-r--r--engines/testbed/savegame.cpp199
-rw-r--r--engines/testbed/savegame.h69
-rw-r--r--engines/testbed/sound.cpp280
-rw-r--r--engines/testbed/sound.h83
-rw-r--r--engines/testbed/template.h67
-rw-r--r--engines/testbed/testbed.cpp192
-rw-r--r--engines/testbed/testbed.h77
-rw-r--r--engines/testbed/testsuite.cpp333
-rw-r--r--engines/testbed/testsuite.h192
-rw-r--r--engines/tinsel/bmv.cpp6
-rw-r--r--engines/tinsel/detection_tables.h2
-rw-r--r--engines/tinsel/music.cpp153
-rw-r--r--engines/tinsel/sound.cpp17
-rw-r--r--engines/tinsel/tinsel.cpp28
-rw-r--r--engines/tinsel/tinsel.h8
-rw-r--r--engines/toon/anim.cpp703
-rw-r--r--engines/toon/anim.h194
-rw-r--r--engines/toon/audio.cpp480
-rw-r--r--engines/toon/audio.h147
-rw-r--r--engines/toon/character.cpp1030
-rw-r--r--engines/toon/character.h144
-rw-r--r--engines/toon/conversation.cpp49
-rw-r--r--engines/toon/conversation.h50
-rw-r--r--engines/toon/detection.cpp268
-rw-r--r--engines/toon/drew.cpp122
-rw-r--r--engines/toon/drew.h51
-rw-r--r--engines/toon/flux.cpp140
-rw-r--r--engines/toon/flux.h52
-rw-r--r--engines/toon/font.cpp274
-rw-r--r--engines/toon/font.h52
-rw-r--r--engines/toon/hotspot.cpp157
-rw-r--r--engines/toon/hotspot.h73
-rw-r--r--engines/toon/module.mk30
-rw-r--r--engines/toon/movie.cpp111
-rw-r--r--engines/toon/movie.h58
-rw-r--r--engines/toon/path.cpp370
-rw-r--r--engines/toon/path.h93
-rw-r--r--engines/toon/picture.cpp296
-rw-r--r--engines/toon/picture.h66
-rw-r--r--engines/toon/resource.cpp215
-rw-r--r--engines/toon/resource.h82
-rw-r--r--engines/toon/script.cpp504
-rw-r--r--engines/toon/script.h152
-rw-r--r--engines/toon/script_func.cpp1154
-rw-r--r--engines/toon/script_func.h171
-rw-r--r--engines/toon/state.cpp261
-rw-r--r--engines/toon/state.h101
-rw-r--r--engines/toon/text.cpp95
-rw-r--r--engines/toon/text.h51
-rw-r--r--engines/toon/tools.cpp516
-rw-r--r--engines/toon/tools.h83
-rw-r--r--engines/toon/toon.cpp4401
-rw-r--r--engines/toon/toon.h388
-rw-r--r--engines/touche/midi.cpp5
649 files changed, 93940 insertions, 6135 deletions
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index f4af4a8500..22212675c7 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -214,10 +214,36 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *
desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
}
+bool cleanupPirated(ADGameDescList &matched) {
+ // OKay, now let's sense presense of pirated games
+ if (!matched.empty()) {
+ for (uint j = 0; j < matched.size();) {
+ if (matched[j]->flags & ADGF_PIRATED)
+ matched.remove_at(j);
+ else
+ ++j;
+ }
+
+ // We ruled out all variants and now have nothing
+ if (matched.empty()) {
+
+ warning("Illegitimate copy of the game detected. We give no support in such cases %d", matched.size());
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
ADGameDescList matches = detectGame(fslist, params, Common::UNK_LANG, Common::kPlatformUnknown, "");
GameList detectedGames;
+ if (cleanupPirated(matches))
+ return detectedGames;
+
// Use fallback detector if there were no matches by other means
if (matches.empty()) {
const ADGameDescription *fallbackDesc = fallbackDetect(fslist);
@@ -282,6 +308,9 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
ADGameDescList matches = detectGame(files, params, language, platform, extra);
+ if (cleanupPirated(matches))
+ return Common::kNoGameDataFoundError;
+
if (params.singleid == NULL) {
for (uint i = 0; i < matches.size(); i++) {
if (matches[i]->gameid == gameid) {
@@ -342,14 +371,14 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz
printf("of the game you tried to add and its version/language/etc.:\n");
for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file)
- printf(" \"%s\", \"%s\", %d\n", file->_key.c_str(), file->_value.md5, file->_value.size);
+ printf(" {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5, file->_value.size);
printf("\n");
}
static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams &params);
-static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char **directoryGlobs) {
+static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char * const *directoryGlobs) {
if (depth <= 0)
return;
@@ -366,12 +395,12 @@ static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles,
continue;
bool matched = false;
- for (const char *glob = *directoryGlobs; *glob; glob++)
- if (file->getName().matchString(glob, true)) {
+ for (const char * const *glob = directoryGlobs; *glob; glob++)
+ if (file->getName().matchString(*glob, true)) {
matched = true;
break;
}
-
+
if (!matched)
continue;
@@ -419,7 +448,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
if (g->flags & ADGF_MACRESFORK) {
Common::MacResManager *macResMan = new Common::MacResManager();
-
+
if (macResMan->open(parent, fname)) {
if (!macResMan->getResForkMD5(tmp.md5, params.md5Bytes))
tmp.md5[0] = 0;
@@ -443,7 +472,7 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
tmp.size = -1;
tmp.md5[0] = 0;
}
-
+
debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5);
filesSizeMD5[fname] = tmp;
}
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 1e59df04bf..0ebb264f74 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -45,6 +45,7 @@ struct ADGameFileDescription {
enum ADGameFlags {
ADGF_NO_FLAGS = 0,
+ ADGF_PIRATED = (1 << 23), // flag to designate well known pirated versions with cracks
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
@@ -207,7 +208,7 @@ struct ADParams {
*
* @note Last item must be 0
*/
- const char **directoryGlobs;
+ const char * const *directoryGlobs;
};
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index e83ef4ead9..0646f321d4 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -501,8 +501,7 @@ AgiBase::AgiBase(OSystem *syst, const AGIGameDescription *gameDesc) : Engine(sys
AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBase(syst, gameDesc) {
// Setup mixer
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ syncSoundSettings();
parseFeatures();
@@ -578,18 +577,25 @@ void AgiEngine::initialize() {
_soundemu = SOUND_EMU_APPLE2GS;
} else if (getPlatform() == Common::kPlatformCoCo3) {
_soundemu = SOUND_EMU_COCO3;
+ } else if (ConfMan.get("music_driver") == "auto") {
+ // Default sound is the proper PCJr emulation
+ _soundemu = SOUND_EMU_PCJR;
} else {
- switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK|MDT_ADLIB|MDT_PCJR|MDT_MIDI))) {
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK|MDT_AMIGA|MDT_ADLIB|MDT_PCJR|MDT_MIDI))) {
case MT_PCSPK:
_soundemu = SOUND_EMU_PC;
break;
+ case MT_ADLIB:
+ _soundemu = SOUND_EMU_NONE;
+ break;
case MT_PCJR:
_soundemu = SOUND_EMU_PCJR;
break;
- case MT_ADLIB:
- _soundemu = SOUND_EMU_NONE;
+ case MT_AMIGA:
+ _soundemu = SOUND_EMU_AMIGA;
break;
default:
+ debug(0, "DEF");
_soundemu = SOUND_EMU_MIDI;
break;
}
@@ -709,18 +715,6 @@ Common::Error AgiEngine::go() {
return Common::kNoError;
}
-void AgiEngine::syncSoundSettings() {
- // FIXME/TODO: Please explain why we are using "music_volume" for all
- // three different entries here.
- int soundVolumeMusic = ConfMan.getInt("music_volume");
- int soundVolumeSFX = ConfMan.getInt("music_volume");
- int soundVolumeSpeech = ConfMan.getInt("music_volume");
-
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
- _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
-}
-
void AgiEngine::parseFeatures() {
if (!ConfMan.hasKey("features"))
return;
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 507e7f7a11..4df8824b0e 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -795,7 +795,6 @@ class AgiEngine : public AgiBase {
protected:
// Engine APIs
virtual Common::Error go();
- virtual void syncSoundSettings();
void initialize();
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index b7eba22298..57bcabb188 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -387,6 +387,8 @@ int AgiEngine::runGame() {
if (_restartGame) {
setflag(fRestartGame, true);
+ _game.lastController = 0;
+ setvar(vTimeDelay, 2); // "normal" speed
_restartGame = false;
}
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index d1bed5d716..14ef169c48 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -435,7 +435,8 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const Common::FSList &fsl
// If there's game last edit date in the *.wag file, add it to extra
if (wagGameLastEdit != NULL) {
- if (!_extra.empty() ) _extra += " ";
+ if (!_extra.empty())
+ _extra += " ";
_extra += wagGameLastEdit->getData();
debug(3, "Agi::fallbackDetector: Game's last edit date (%s) from WAG file", wagGameLastEdit->getData());
}
diff --git a/engines/agi/inv.cpp b/engines/agi/inv.cpp
index da56449fda..46dfcb2b43 100644
--- a/engines/agi/inv.cpp
+++ b/engines/agi/inv.cpp
@@ -114,7 +114,7 @@ void AgiEngine::selectItems(int n) {
int fsel = 0;
bool exit_select = false;
- while (!exit_select) {
+ while (!exit_select && !(shouldQuit() || _restartGame)) {
if (n > 0)
printItem(fsel, STATUS_BG, STATUS_FG);
diff --git a/engines/agi/lzw.cpp b/engines/agi/lzw.cpp
index 60bd8f4fca..f645cb16d3 100644
--- a/engines/agi/lzw.cpp
+++ b/engines/agi/lzw.cpp
@@ -107,7 +107,7 @@ uint8 *LZWDecoder::decodeString(uint8 *buffer, uint32 code) {
*buffer++ = appendCharacter[code];
code = prefixCode[code];
if (i++ >= 4000) {
- error("lzw: error in code expansion.");
+ error("lzw: error in code expansion");
}
}
*buffer = code;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index a60080186c..1627f03863 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -497,6 +497,7 @@ void AgiEngine::cmd_script_size(uint8 *p) {
void AgiEngine::cmd_cancel_line(uint8 *p) {
_game.inputBuffer[0] = 0;
+ _game.cursorPos = 0;
writePrompt();
}
diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp
index dc77433cb2..47b72cc8c6 100644
--- a/engines/agi/picture.cpp
+++ b/engines/agi/picture.cpp
@@ -562,7 +562,7 @@ void PictureMgr::drawPicture() {
_patCode = 0;
_patNum = 0;
_priOn = _scrOn = false;
- _scrColor = 0xf;
+ _scrColor = (_pictureVersion == AGIPIC_C64) ? 0x0 : 0xf;
_priColor = 0x4;
drawing = 1;
diff --git a/engines/agi/predictive.cpp b/engines/agi/predictive.cpp
index 153fec641a..414477a381 100644
--- a/engines/agi/predictive.cpp
+++ b/engines/agi/predictive.cpp
@@ -210,13 +210,12 @@ bool AgiEngine::predictiveDialog() {
}
}
- temp[MAXWORDLEN] = 0;
-
- strncpy(temp, prefix.c_str(), MAXWORDLEN);
- strncat(temp, _currentWord.c_str(), MAXWORDLEN);
+ Common::strlcpy(temp, prefix.c_str(), sizeof(temp));
+ Common::strlcat(temp, _currentWord.c_str(), sizeof(temp));
for (int i = prefix.size() + _currentCode.size(); i < MAXWORDLEN; i++)
temp[i] = ' ';
+ temp[MAXWORDLEN] = 0;
printText(temp, 0, 8, 7, MAXWORDLEN, 15, 0);
_gfx->flushBlock(62, 54, 249, 66);
@@ -461,9 +460,8 @@ bool AgiEngine::predictiveDialog() {
}
press:
- strncpy(_predictiveResult, prefix.c_str(), 40);
- strncat(_predictiveResult, _currentWord.c_str(), 40);
- _predictiveResult[prefix.size() + _currentCode.size() + 1] = 0;
+ Common::strlcpy(_predictiveResult, prefix.c_str(), sizeof(_predictiveResult));
+ Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult));
getout:
// if another window was shown, bring it up again
@@ -518,7 +516,7 @@ void AgiEngine::loadDict() {
_predictiveDictLine = (char **)calloc(1, sizeof(char *) * lines);
if (_predictiveDictLine == NULL) {
- warning("Cannot allocate memory for line index buffer.");
+ warning("Cannot allocate memory for line index buffer");
return;
}
_predictiveDictLine[0] = _predictiveDictText;
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index 88b14dcfe2..1a968816d4 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -333,7 +333,7 @@ int AgiEngine::loadGame(const char *fileName, bool checkId) {
debug(0, "Saved game MD5: \"%s\"", md5);
if (!getGameMD5()) {
- warning("Since your game was only detected via the fallback detector, there is no possibility to assure the save is compatible with your game version.");
+ warning("Since your game was only detected via the fallback detector, there is no possibility to assure the save is compatible with your game version");
debug(0, "The game used for saving is \"%s\".", md5);
} else if (strcmp(md5, getGameMD5())) {
@@ -809,12 +809,11 @@ int AgiEngine::saveGameDialog() {
printText("Select a slot in which you wish to\nsave the game:",
0, hm + 1, vm + 1, w, MSG_BOX_TEXT, MSG_BOX_COLOUR);
slot = selectSlot();
- if (slot == 0)
+ if (slot + _firstSlot == 0)
messageBox("That slot is for Autosave only.");
else if (slot < 0)
return errOK;
- }
- while (slot == 0);
+ } while (slot + _firstSlot == 0);
drawWindow(hp, vp + 5 * CHAR_LINES, GFX_WIDTH - hp,
GFX_HEIGHT - vp - 9 * CHAR_LINES);
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
index cb4e307ea6..b215822917 100644
--- a/engines/agi/sound.cpp
+++ b/engines/agi/sound.cpp
@@ -176,9 +176,9 @@ SoundMgr::SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer) {
case SOUND_EMU_NONE:
case SOUND_EMU_AMIGA:
case SOUND_EMU_MAC:
+ case SOUND_EMU_PC:
_soundGen = new SoundGenSarien(_vm, pMixer);
break;
- case SOUND_EMU_PC:
case SOUND_EMU_PCJR:
_soundGen = new SoundGenPCJr(_vm, pMixer);
break;
diff --git a/engines/agi/sound_midi.cpp b/engines/agi/sound_midi.cpp
index 57c5d54b27..0c8b3fa36a 100644
--- a/engines/agi/sound_midi.cpp
+++ b/engines/agi/sound_midi.cpp
@@ -74,8 +74,15 @@ SoundGenMIDI::SoundGenMIDI(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, p
DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
_driver = MidiDriver::createMidi(dev);
+ if (ConfMan.getBool("native_mt32") || MidiDriver::getMusicType(dev) == MT_MT32) {
+ _nativeMT32 = true;
+ _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ } else {
+ _nativeMT32 = false;
+ }
+
memset(_channel, 0, sizeof(_channel));
- memset(_channelVolume, 255, sizeof(_channelVolume));
+ memset(_channelVolume, 127, sizeof(_channelVolume));
_masterVolume = 0;
this->open();
_smfParser = MidiParser::createParser_SMF();
@@ -121,6 +128,12 @@ int SoundGenMIDI::open() {
return ret;
_driver->setTimerCallback(this, &onTimer);
+
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
return 0;
}
diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp
index b9d701d7f7..35c960d260 100644
--- a/engines/agi/sound_pcjr.cpp
+++ b/engines/agi/sound_pcjr.cpp
@@ -125,6 +125,9 @@ SoundGenPCJr::SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, p
_dissolveMethod = 3;
+ memset(_channel, 0, sizeof(_channel));
+ memset(_tchannel, 0, sizeof(_tchannel));
+
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
}
@@ -158,7 +161,7 @@ void SoundGenPCJr::play(int resnum) {
void SoundGenPCJr::stop(void) {
int i;
- for (i = 0; i < CHAN_MAX ; i++) {
+ for (i = 0; i < CHAN_MAX; i++) {
_channel[i].avail = 0;
_tchannel[i].avail = 0;
}
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index c5841ff05e..0c8e6dd63c 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -561,11 +561,11 @@ Common::Error AGOSEngine::init() {
_driver = MidiDriver::createMidi(dev);
- if (_nativeMT32) {
+ if (_nativeMT32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- }
- _midi.mapMT32toGM (getGameType() != GType_SIMON2 && !_nativeMT32);
+ _midi.setNativeMT32(_nativeMT32);
+ _midi.mapMT32toGM(getGameType() != GType_SIMON2 && !_nativeMT32);
_midi.setDriver(_driver);
@@ -925,10 +925,10 @@ AGOSEngine::~AGOSEngine() {
free(_textMem);
free(_xtblList);
- free(_backGroundBuf);
- free(_backBuf);
+ delete _backGroundBuf;
+ delete _backBuf;
free(_planarBuf);
- free(_scaleBuf);
+ delete _scaleBuf;
free(_zoneBuffers);
free(_window4BackScn);
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp
index 1b3ac9fd65..af85c50114 100644
--- a/engines/agos/animation.cpp
+++ b/engines/agos/animation.cpp
@@ -372,10 +372,10 @@ bool MoviePlayerDXA::processFrame() {
_vm->_system->unlockScreen();
Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000);
- if ((_bgSoundStream == NULL) || ((int)(soundTime * getFrameRate()) / 1000 < getCurFrame() + 1)) {
+ if ((_bgSoundStream == NULL) || ((soundTime * getFrameRate()).toInt() / 1000 < getCurFrame() + 1)) {
if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) {
- while (_mixer->isSoundHandleActive(_bgSound) && ((int) (soundTime * getFrameRate())) < getCurFrame()) {
+ while (_mixer->isSoundHandleActive(_bgSound) && (soundTime * getFrameRate()).toInt() < getCurFrame()) {
_vm->_system->delayMillis(10);
soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000);
}
diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h
index e3709f8409..963c08849c 100644
--- a/engines/agos/detection_tables.h
+++ b/engines/agos/detection_tables.h
@@ -2811,6 +2811,27 @@ static const AGOSGameDescription gameDescriptions[] = {
GF_OLD_BUNDLE | GF_TALKIE
},
+ // Simon the Sorcerer's Puzzle Pack - Swampy Adventures - Polish
+ {
+ {
+ "swampy",
+ "CD",
+
+ {
+ { "Gswampy", GAME_BASEFILE, "31bfb5169b47ccc19177e61bd31d4391", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::PL_POL,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+
+ GType_PP,
+ GID_SWAMPY,
+ GF_OLD_BUNDLE | GF_TALKIE
+ },
+
// Simon the Sorcerer's Puzzle Pack - Swampy Adventures - Spanish
{
{
diff --git a/engines/agos/items.cpp b/engines/agos/items.cpp
index 874bf1a802..6c6b127a51 100644
--- a/engines/agos/items.cpp
+++ b/engines/agos/items.cpp
@@ -429,6 +429,9 @@ Item *AGOSEngine::findMaster(int16 a, int16 n) {
for (j = 1; j < _itemArraySize; j++) {
Item *item = derefItem(j);
+ if (item == NULL)
+ continue;
+
if (wordMatch(item, a, n))
return item;
}
@@ -442,6 +445,9 @@ Item *AGOSEngine::nextMaster(Item *i, int16 a, int16 n) {
for (j = first; j < _itemArraySize; j++) {
Item *item = derefItem(j);
+ if (item == NULL)
+ continue;
+
if (wordMatch(item, a, n))
return item;
}
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index ab5bfc4c94..858307685c 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -77,10 +77,10 @@ int MidiPlayer::open() {
return ret;
_driver->setTimerCallback(this, &onTimer);
- // General MIDI System On message
- // Resets all GM devices to default settings
- _driver->sysEx((const byte *)"\x7E\x7F\x09\x01", 4);
- g_system->delayMillis(20);
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
return 0;
}
diff --git a/engines/agos/midi.h b/engines/agos/midi.h
index d4c09118f6..d76997737a 100644
--- a/engines/agos/midi.h
+++ b/engines/agos/midi.h
@@ -61,6 +61,7 @@ protected:
MidiDriver *_driver;
bool _map_mt32_to_gm;
bool _passThrough;
+ bool _nativeMT32;
MusicInfo _music;
MusicInfo _sfx;
@@ -97,6 +98,7 @@ public:
void loadS1D(Common::File *in, bool sfx = false);
void mapMT32toGM(bool map);
+ void setNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; }
void setLoop(bool loop);
void startTrack(int track);
void queueTrack(int track, bool loop);
diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp
index e9fbaf3525..ffa95506c5 100644
--- a/engines/agos/saveload.cpp
+++ b/engines/agos/saveload.cpp
@@ -26,6 +26,7 @@
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
+#include "common/translation.h"
#include "gui/about.h"
#include "gui/message.h"
@@ -146,14 +147,14 @@ void AGOSEngine::quickLoadOrSave() {
}
bool success;
- char buf[60];
+ Common::String buf;
char *filename = genSaveName(_saveLoadSlot);
if (_saveLoadType == 2) {
Subroutine *sub;
success = loadGame(genSaveName(_saveLoadSlot));
if (!success) {
- sprintf(buf, "Failed to load game state to file:\n\n%s", filename);
+ buf = Common::String::printf(_("Failed to load game state from file:\n\n%s"), filename);
} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
drawIconArray(2, me(), 0, 0);
setBitFlag(97, true);
@@ -188,7 +189,7 @@ void AGOSEngine::quickLoadOrSave() {
} else {
success = saveGame(_saveLoadSlot, _saveLoadName);
if (!success)
- sprintf(buf, "Failed to save game state to file:\n\n%s", filename);
+ buf = Common::String::printf(_("Failed to save game state to file:\n\n%s"), filename);
}
if (!success) {
@@ -196,7 +197,7 @@ void AGOSEngine::quickLoadOrSave() {
dialog.runModal();
} else if (_saveLoadType == 1) {
- sprintf(buf, "Successfully saved game state in file:\n\n%s", filename);
+ buf = Common::String::printf(_("Successfully saved game state in file:\n\n%s"), filename);
GUI::TimedMessageDialog dialog(buf, 1500);
dialog.runModal();
diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp
index bd4c89a404..6ec72814fa 100644
--- a/engines/agos/sound.cpp
+++ b/engines/agos/sound.cpp
@@ -776,7 +776,7 @@ void Sound::playVoiceData(byte *soundData, uint sound) {
}
void Sound::playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan, int vol, bool loop) {
- int size = READ_LE_UINT32(soundData + 4);
+ int size = READ_LE_UINT32(soundData + 4) + 8;
Common::MemoryReadStream *stream = new Common::MemoryReadStream(soundData, size);
Audio::RewindableAudioStream *sndStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
diff --git a/engines/cine/pal.cpp b/engines/cine/pal.cpp
index 27d0e593da..34b196d5c7 100644
--- a/engines/cine/pal.cpp
+++ b/engines/cine/pal.cpp
@@ -186,8 +186,7 @@ const Graphics::PixelFormat &Palette::colorFormat() const {
void Palette::setGlobalOSystemPalette() const {
byte buf[256 * 4]; // Allocate space for the largest possible palette
// The color format used by OSystem's setPalette-function:
- static const Graphics::PixelFormat kSystemPalFormat(4, 8, 8, 8, 0, 0, 8, 16, 0);
- save(buf, sizeof(buf), kSystemPalFormat, CINE_LITTLE_ENDIAN);
+ save(buf, sizeof(buf), Graphics::PixelFormat(4, 8, 8, 8, 0, 0, 8, 16, 0), CINE_LITTLE_ENDIAN);
if (g_cine->getPlatform() == Common::kPlatformAmiga && colorCount() == 16) {
// The Amiga version of Future Wars does use the upper 16 colors for a darkened
diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp
index 95f3789abd..f5c9402388 100644
--- a/engines/cine/part.cpp
+++ b/engines/cine/part.cpp
@@ -164,7 +164,7 @@ void CineEngine::readVolCnf() {
// All file name blocks' sizes were divisible by either 11 or 13, but not with both.
fileNameLength = (fileNameLenMod11 ? 11 : 13);
} else {
- warning("Couldn't deduce file name length from data in 'vol.cnf', using a backup deduction scheme.");
+ warning("Couldn't deduce file name length from data in 'vol.cnf', using a backup deduction scheme");
// Here we use the former file name length detection method
// if we couldn't deduce the file name length from the data.
fileNameLength = (compressed ? 11 : 13);
diff --git a/engines/cruise/menu.cpp b/engines/cruise/menu.cpp
index 2ffaa29e4a..e5a1136c23 100644
--- a/engines/cruise/menu.cpp
+++ b/engines/cruise/menu.cpp
@@ -29,6 +29,7 @@
#include "engines/metaengine.h"
#include "gui/saveload.h"
+#include "common/translation.h"
namespace Cruise {
@@ -212,9 +213,9 @@ static void handleSaveLoad(bool saveFlag) {
EngineMan.findGame(_vm->getGameId(), &plugin);
GUI::SaveLoadChooser *dialog;
if (saveFlag)
- dialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"));
else
- dialog = new GUI::SaveLoadChooser("Load game:", "Load");
+ dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"));
dialog->setSaveMode(saveFlag);
int slot = dialog->runModal(plugin, ConfMan.getActiveDomainName());
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index 2397a474c6..cb081e4683 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -106,7 +106,10 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd);
- _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _("~R~eturn to Launcher"), 0, kRTLCmd);
+ if (g_system->getOverlayWidth() > 320)
+ _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _("~R~eturn to Launcher"), 0, kRTLCmd);
+ else
+ _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _c("~R~eturn to Launcher", "lowres"), 0, kRTLCmd);
_rtlButton->setEnabled(_engine->hasFeature(Engine::kSupportsRTL));
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index cd3920b30d..814159dbbb 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -170,6 +170,7 @@ int DraciEngine::init() {
_music = new MusicPlayer(_midiDriver, musicPathMask);
_music->setNativeMT32(native_mt32);
+ _music->open();
//_music->setAdLib(adlib);
// Load the game's fonts
diff --git a/engines/draci/music.cpp b/engines/draci/music.cpp
index 8186d36068..95b7ae08da 100644
--- a/engines/draci/music.cpp
+++ b/engines/draci/music.cpp
@@ -38,15 +38,10 @@ namespace Draci {
MusicPlayer::MusicPlayer(MidiDriver *driver, const char *pathMask) : _parser(0), _driver(driver), _pathMask(pathMask), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false), _track(-1) {
memset(_channel, 0, sizeof(_channel));
- memset(_channelVolume, 255, sizeof(_channelVolume));
+ memset(_channelVolume, 127, sizeof(_channelVolume));
_masterVolume = 0;
- this->open();
_smfParser = MidiParser::createParser_SMF();
_midiMusicData = NULL;
-
- // TODO: Load cmf.ins with the instrument table. It seems that an
- // interface for such an operation is supported for AdLib. Maybe for
- // this card, setting instruments is necessary.
}
MusicPlayer::~MusicPlayer() {
@@ -89,6 +84,15 @@ int MusicPlayer::open() {
if (ret)
return ret;
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
+ // TODO: Load cmf.ins with the instrument table. It seems that an
+ // interface for such an operation is supported for AdLib. Maybe for
+ // this card, setting instruments is necessary.
+
_driver->setTimerCallback(this, &onTimer);
return 0;
}
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index 7a6a68618d..f657dfe33c 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -967,7 +967,7 @@ int Script::handleMathExpression(Common::MemoryReadStream *reader) const {
func = _functionList[value-1];
// If not yet implemented
- if (func._handler == NULL) {
+ if (func._handler == 0) {
stk.pop();
// Pushing dummy value
@@ -1170,7 +1170,7 @@ void Script::run(const GPL2Program &program, uint16 offset) {
GPLHandler handler = cmd->_handler;
- if (handler != NULL) {
+ if (handler != 0) {
// Call the handler for the current command
(this->*(cmd->_handler))(params);
}
diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp
index d6a3bafd9f..10f1bf4651 100644
--- a/engines/drascula/animation.cpp
+++ b/engines/drascula/animation.cpp
@@ -761,17 +761,10 @@ void DrasculaEngine::animation_16_2() {
clearRoom();
- // FIXME: Track 31 is missing from the soundtrack available
- // from ScummVM's downloads page, so for now we're using the
- // Spanish track 29
-#if 1
- playMusic(30);
-#else
if (_lang == kSpanish)
playMusic(30);
else
playMusic(32);
-#endif
if (getScan() != 0)
goto asco;
diff --git a/engines/drascula/converse.cpp b/engines/drascula/converse.cpp
index 0e70348148..d0906fdf55 100644
--- a/engines/drascula/converse.cpp
+++ b/engines/drascula/converse.cpp
@@ -211,6 +211,7 @@ void DrasculaEngine::converse(int index) {
}
updateEvents();
+ flushKeyBuffer();
phrase1_bottom = 8 * print_abc_opc(phrase1, 2, game1);
phrase2_bottom = phrase1_bottom + 8 * print_abc_opc(phrase2, phrase1_bottom + 2, game2);
@@ -287,8 +288,12 @@ void DrasculaEngine::response(int function) {
playTalkSequence(function);
if (currentChapter == 2) {
- if (function == 16 || function == 20 || function == 23 || function == 29 || function == 31)
+ bool reloadConversationCharset = false;
+
+ if (function == 16 || function == 20 || function == 23 || function == 29 || function == 31) {
+ reloadConversationCharset = true;
loadPic(menuBackground, backSurface);
+ }
if (function == 16)
animation_16_2();
@@ -300,6 +305,9 @@ void DrasculaEngine::response(int function) {
animation_29_2();
else if (function == 31)
animation_31_2();
+
+ if (reloadConversationCharset)
+ loadPic("car.alg", backSurface);
} else if (currentChapter == 3) {
grr();
}
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index c10222cadd..0dafcdc3cd 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -240,6 +240,23 @@ static const DrasculaGameDescription gameDescriptions[] = {
GUIO_NONE
},
},
+
+ {
+ // Drascula French version (ScummVM repacked files)
+ {
+ "drascula",
+ 0,
+ {
+ {"packet.001", 0, "c6a8697396e213a18472542d5f547cb4", 32847563},
+ {"packet.002", 1, "7b83cedb9bb326ed5143e5c459508d43", 722383},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ GF_PACKED,
+ GUIO_NONE
+ },
+ },
{ AD_TABLE_END_MARKER }
};
diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp
index 1495694a1b..1d17c77f83 100644
--- a/engines/drascula/interface.cpp
+++ b/engines/drascula/interface.cpp
@@ -120,7 +120,7 @@ void DrasculaEngine::showMenu() {
x = whichObject();
strcpy(textIcon, iconName[x]);
- for (n = 1; n < 43; n++) {
+ for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
h = inventoryObjects[n];
if (h != 0) {
@@ -194,11 +194,10 @@ void DrasculaEngine::enterName() {
}
bool DrasculaEngine::checkMenuFlags() {
- for (int n = 0; n < 43; n++) {
- if (whichObject() == n) {
- if (inventoryObjects[n] != 0 && checkAction(inventoryObjects[n]))
- return true;
- }
+ int n = whichObject();
+ if (n != 0) {
+ if (inventoryObjects[n] != 0 && checkAction(inventoryObjects[n]))
+ return true;
}
return false;
diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp
index 73aea7b7f2..2bd1014083 100644
--- a/engines/drascula/objects.cpp
+++ b/engines/drascula/objects.cpp
@@ -221,16 +221,17 @@ void DrasculaEngine::addObject(int obj) {
* If no inventory slot is under the mouse cursor, return 0.
*/
int DrasculaEngine::whichObject() {
- int n = 0;
+ int n;
for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) {
if (mouseX > _itemLocations[n].x && mouseY > _itemLocations[n].y &&
mouseX < _itemLocations[n].x + OBJWIDTH &&
- mouseY < _itemLocations[n].y + OBJHEIGHT)
- break;
+ mouseY < _itemLocations[n].y + OBJHEIGHT) {
+ return n;
+ }
}
- return n;
+ return 0;
}
void DrasculaEngine::updateVisible() {
diff --git a/engines/drascula/saveload.cpp b/engines/drascula/saveload.cpp
index abf17d0e8e..4aaec5ec0e 100644
--- a/engines/drascula/saveload.cpp
+++ b/engines/drascula/saveload.cpp
@@ -210,7 +210,7 @@ bool DrasculaEngine::loadGame(const char *gameName) {
curY = sav->readSint32LE();
trackProtagonist = sav->readSint32LE();
- for (l = 1; l < 43; l++) {
+ for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) {
inventoryObjects[l] = sav->readSint32LE();
}
@@ -241,7 +241,7 @@ void DrasculaEngine::saveGame(char gameName[]) {
out->writeSint32LE(curY);
out->writeSint32LE(trackProtagonist);
- for (l = 1; l < 43; l++) {
+ for (l = 1; l < ARRAYSIZE(inventoryObjects); l++) {
out->writeSint32LE(inventoryObjects[l]);
}
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 627de87723..14715ccaac 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -110,7 +110,7 @@ Engine::Engine(OSystem *syst)
// heaps of (sound) memory get allocated but never freed. Of course,
// there still would be problems with many games...
if (!_mixer->isReady())
- warning("Sound initialization failed. This may cause severe problems in some games.");
+ warning("Sound initialization failed. This may cause severe problems in some games");
// Setup a dummy cursor and palette, so that all engines can use
// CursorMan.replace without having any headaches about memory leaks.
diff --git a/engines/engines.mk b/engines/engines.mk
index 2c1378290c..be119c35d6 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -60,6 +60,11 @@ DEFINES += -DENABLE_GROOVIE2
endif
endif
+ifdef ENABLE_HUGO
+DEFINES += -DENABLE_HUGO=$(ENABLE_HUGO)
+MODULES += engines/hugo
+endif
+
ifdef ENABLE_KYRA
DEFINES += -DENABLE_KYRA=$(ENABLE_KYRA)
MODULES += engines/kyra
@@ -136,6 +141,16 @@ DEFINES += -DENABLE_SWORD2=$(ENABLE_SWORD2)
MODULES += engines/sword2
endif
+ifdef ENABLE_SWORD25
+DEFINES += -DENABLE_SWORD25=$(ENABLE_SWORD25)
+MODULES += engines/sword25
+endif
+
+ifdef ENABLE_TESTBED
+DEFINES += -DENABLE_TESTBED=$(ENABLE_TESTBED)
+MODULES += engines/testbed
+endif
+
ifdef ENABLE_TEENAGENT
DEFINES += -DENABLE_TEENAGENT=$(ENABLE_TEENAGENT)
MODULES += engines/teenagent
@@ -146,6 +161,11 @@ DEFINES += -DENABLE_TINSEL=$(ENABLE_TINSEL)
MODULES += engines/tinsel
endif
+ifdef ENABLE_TOON
+DEFINES += -DENABLE_TOON=$(ENABLE_TOON)
+MODULES += engines/toon
+endif
+
ifdef ENABLE_TOUCHE
DEFINES += -DENABLE_TOUCHE=$(ENABLE_TOUCHE)
MODULES += engines/touche
diff --git a/engines/gob/demos/demoplayer.cpp b/engines/gob/demos/demoplayer.cpp
index 25cd470f04..4ceca3ce24 100644
--- a/engines/gob/demos/demoplayer.cpp
+++ b/engines/gob/demos/demoplayer.cpp
@@ -123,7 +123,7 @@ void DemoPlayer::init() {
void DemoPlayer::clearScreen() {
debugC(1, kDebugDemo, "Clearing the screen");
- _vm->_video->clearSurf(*_vm->_draw->_backSurface);
+ _vm->_draw->_backSurface->clear();
_vm->_draw->forceBlit();
_vm->_video->retrace();
}
@@ -244,10 +244,11 @@ void DemoPlayer::playVideoDoubled(int slot) {
int16 wD = (rect->left * 2) + (w * 2);
int16 hD = (rect->top * 2) + (h * 2);
- _vm->_video->drawSpriteDouble(*_vm->_draw->_spritesArray[0], *_vm->_draw->_frontSurface,
- rect->left, rect->top, rect->right - 1, rect->bottom - 1, rect->left, rect->top, 0);
- _vm->_draw->dirtiedRect(_vm->_draw->_frontSurface,
- rect->left * 2, rect->top * 2, wD, hD);
+ _vm->_draw->_frontSurface->blitScaled(*_vm->_draw->_spritesArray[0],
+ rect->left, rect->top, rect->right - 1, rect->bottom - 1, rect->left * 2, rect->top * 2, 2);
+
+ _vm->_draw->dirtiedRect(_vm->_draw->_frontSurface,
+ rect->left * 2, rect->top * 2, wD, hD);
}
}
@@ -297,10 +298,10 @@ void DemoPlayer::evaluateVideoMode(const char *mode) {
_doubleMode = false;
// Only applicable when we actually can double
- if (_vm->is640()) {
- if (!scumm_strnicmp(mode, "AUTO", 4))
+ if (_vm->is640x480() || _vm->is800x600()) {
+ if (!scumm_strnicmp(mode, "AUTO", 4))
_autoDouble = true;
- else if (!scumm_strnicmp(mode, "VGA", 3) && _vm->is640())
+ else if (!scumm_strnicmp(mode, "VGA", 3))
_doubleMode = true;
}
}
diff --git a/engines/gob/detection_tables.h b/engines/gob/detection_tables.h
index 20edb9fbc3..93c1cc6d1c 100644
--- a/engines/gob/detection_tables.h
+++ b/engines/gob/detection_tables.h
@@ -420,6 +420,20 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesAdLib,
0, 0, 0
},
+ { // Provided by pykman in the forums.
+ {
+ "gob1cd",
+ "Polish",
+ AD_ENTRY1s("intro.stk", "97d2443948b2e367cf567fe7e101f5f2", 4049267),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
{ // CD 1.000 version.
{
"gob1cd",
@@ -1054,6 +1068,20 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesCD,
0, 0, 0
},
+ { // Supplied by pykman in bug report #3067489
+ {
+ "gob2cd",
+ "v2.01 Polish",
+ AD_ENTRY1s("intro.stk", "3025f05482b646c18c2c79c615a3a1df", 5011726),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
{
{
"gob2cd",
@@ -1798,6 +1826,22 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesAdLib | kFeaturesEGA,
0, 0, 0
},
+// This version is not detected on purpose: it's a pirated version.
+// Tagged ADGF_PIRATED! Do not re-add nor un-tag!
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "3712e7527ba8ce5637d2aadf62783005", 72318),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_PIRATED,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
{
{
"lit",
@@ -2220,18 +2264,20 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesAdLib,
"demo.stk", "demo.tot", 0
},
+// This version is not detected on purpose: it's a pirated version, using a corrupted crack.
+// Tagged ADGF_PIRATED! Do not re-add nor un-tag!
{
{
"fascination",
- "CD Version (Censored)",
- AD_ENTRY1s("disk0.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
- EN_ANY,
+ "",
+ AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
+ UNK_LANG,
kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES
+ ADGF_PIRATED,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeFascination,
- kFeaturesCD,
+ kFeaturesAdLib,
"disk0.stk", 0, 0
},
{
@@ -2239,7 +2285,7 @@ static const GOBGameDescription gameDescriptions[] = {
"fascination",
"VGA 3 disks edition",
AD_ENTRY1s("disk0.stk", "a50a8495e1b2d67699fb562cb98fc3e2", 1064387),
- UNK_LANG,
+ FR_FRA,
kPlatformPC,
ADGF_NO_FLAGS,
GUIO_NOSUBTITLES | GUIO_NOSPEECH
@@ -2251,7 +2297,7 @@ static const GOBGameDescription gameDescriptions[] = {
{
{
"fascination",
- "VGA 3 disks edition",
+ "Hebrew edition (censored)",
AD_ENTRY1s("intro.stk", "d6e45ce548598727e2b5587a99718eba", 1055909),
HE_ISR,
kPlatformPC,
@@ -2262,20 +2308,6 @@ static const GOBGameDescription gameDescriptions[] = {
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",
@@ -2376,6 +2408,76 @@ static const GOBGameDescription gameDescriptions[] = {
},
{
{
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("intro.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "intro.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("intro.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "intro.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("intro.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "intro.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("intro.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "intro.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("intro.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "intro.stk", 0, 0
+ },
+ {
+ {
"geisha",
"",
AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153),
@@ -2724,6 +2826,20 @@ static const GOBGameDescription gameDescriptions[] = {
kFeaturesCD,
0, 0, 0
},
+ { // Supplied by pykman in bug report #3067489
+ {
+ "gob3cd",
+ "v1.02 Polish",
+ AD_ENTRY1s("intro.stk", "978afddcac81bb95a04757b61f78471c", 619825),
+ UNK_LANG,
+ 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",
@@ -3126,7 +3242,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3140,7 +3256,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3154,7 +3270,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3168,7 +3284,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3182,7 +3298,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3196,7 +3312,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3210,7 +3326,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3224,7 +3340,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3238,7 +3354,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3252,7 +3368,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3266,7 +3382,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3280,7 +3396,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3294,7 +3410,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3308,7 +3424,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3322,7 +3438,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3336,7 +3452,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3350,7 +3466,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3364,7 +3480,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3378,7 +3494,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3392,7 +3508,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3406,7 +3522,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3420,7 +3536,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3434,7 +3550,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by DjDiabolik in bug report #1971294
@@ -3448,7 +3564,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ // Supplied by goodoldgeorg in bug report #2098838
@@ -3462,7 +3578,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3480,7 +3596,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 1
},
{
@@ -3494,7 +3610,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3508,7 +3624,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3522,7 +3638,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3536,7 +3652,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3550,7 +3666,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3564,7 +3680,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -3578,7 +3694,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
"lda1.stk", 0, 0
},
{
@@ -3592,7 +3708,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeDynasty,
- kFeatures640,
+ kFeatures640x480,
"lda1.stk", 0, 0
},
{
@@ -3606,7 +3722,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by gamin in the forums
@@ -3620,7 +3736,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by jvprat on #scummvm
@@ -3634,7 +3750,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{ // Supplied by goodoldgeorg in bug report #2770340
@@ -3648,7 +3764,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeatures640,
+ kFeatures640x480 | kFeaturesTrueColor,
0, 0, 0
},
{
@@ -3667,7 +3783,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeUrban,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesTrueColor | kFeaturesSCNDemo,
0, 0, 2
},
{
@@ -3685,7 +3801,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3703,7 +3819,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3721,7 +3837,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by scoriae in the forums
@@ -3739,7 +3855,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3762,7 +3878,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 3
},
{
@@ -3780,7 +3896,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 4
},
{
@@ -3802,7 +3918,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 5
},
{
@@ -3823,7 +3939,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 6
},
{
@@ -3841,7 +3957,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3859,7 +3975,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3877,7 +3993,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by scoriae in the forums
@@ -3895,7 +4011,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3913,7 +4029,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3931,7 +4047,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3949,7 +4065,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -3967,7 +4083,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ // Supplied by Hkz on #scummvm
@@ -3985,7 +4101,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4003,7 +4119,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{ //Supplied by goodoldgeorg in bug report #2820006
@@ -4021,7 +4137,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4039,7 +4155,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4057,7 +4173,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeBambou,
- kFeatures640,
+ kFeatures640x480,
"intro.stk", "intro.tot", 0
},
{
@@ -4075,7 +4191,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4093,7 +4209,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4111,7 +4227,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
"intro2.stk", 0, 0
},
{
@@ -4139,7 +4255,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{ // Found in french ADI 2 Francais-Maths CE2. Exact version not specified.
@@ -4153,7 +4269,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4181,7 +4297,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4195,7 +4311,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4209,7 +4325,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4223,7 +4339,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4237,7 +4353,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4251,7 +4367,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", "ediintro.tot", 0
},
{
@@ -4271,7 +4387,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi2,
- kFeatures640 | kFeaturesSCNDemo,
+ kFeatures640x480 | kFeaturesSCNDemo,
0, 0, 1
},
{
@@ -4285,7 +4401,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4299,7 +4415,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4313,7 +4429,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4327,7 +4443,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4341,7 +4457,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4355,7 +4471,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4411,7 +4527,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4439,7 +4555,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4453,7 +4569,7 @@ static const GOBGameDescription gameDescriptions[] = {
GUIO_NONE
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{
@@ -4751,7 +4867,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeWoodruff,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //9
@@ -4807,7 +4923,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeUrban,
- kFeaturesCD,
+ kFeaturesCD | kFeaturesTrueColor,
0, 0, 0
},
{ //13
@@ -4821,7 +4937,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //14
@@ -4835,7 +4951,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //15
@@ -4849,7 +4965,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //16
@@ -4863,7 +4979,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //17
@@ -4877,7 +4993,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //18
@@ -4891,7 +5007,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypePlaytoons,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //19
@@ -4905,7 +5021,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeBambou,
- kFeatures640,
+ kFeatures640x480,
0, 0, 0
},
{ //20
@@ -4947,7 +5063,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi2,
- kFeatures640,
+ kFeatures640x480,
"adi2.stk", 0, 0
},
{ //23
@@ -4961,7 +5077,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NOSUBTITLES | GUIO_NOSPEECH
},
kGameTypeAdi4,
- kFeatures640,
+ kFeatures640x480,
"adif41.stk", 0, 0
},
{ //24
@@ -4975,7 +5091,7 @@ static const GOBGameDescription fallbackDescs[] = {
GUIO_NONE
},
kGameTypeUrban,
- kFeaturesAdLib | kFeatures640 | kFeaturesSCNDemo,
+ kFeaturesAdLib | kFeatures640x480 | kFeaturesSCNDemo,
"", "", 8
}
};
diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp
index b572ccb566..960f4e9e34 100644
--- a/engines/gob/draw.cpp
+++ b/engines/gob/draw.cpp
@@ -263,10 +263,10 @@ void Draw::blitInvalidated() {
_vm->_video->_doRangeClamp = false;
for (int i = 0; i < _invalidatedCount; i++) {
- _vm->_video->drawSprite(*_backSurface, *_frontSurface,
+ _frontSurface->blit(*_backSurface,
_invalidatedLefts[i], _invalidatedTops[i],
_invalidatedRights[i], _invalidatedBottoms[i],
- _invalidatedLefts[i], _invalidatedTops[i], 0);
+ _invalidatedLefts[i], _invalidatedTops[i]);
_vm->_video->dirtyRectsAdd(_invalidatedLefts[i], _invalidatedTops[i],
_invalidatedRights[i], _invalidatedBottoms[i]);
}
@@ -300,7 +300,7 @@ void Draw::dirtiedRect(int16 surface,
dirtiedRect(_spritesArray[surface], left, top, right, bottom);
}
-void Draw::dirtiedRect(SurfaceDescPtr surface,
+void Draw::dirtiedRect(SurfacePtr surface,
int16 left, int16 top, int16 right, int16 bottom) {
if (surface == _backSurface)
@@ -316,7 +316,7 @@ void Draw::initSpriteSurf(int16 index, int16 width, int16 height,
_spritesArray[index] =
_vm->_video->initSurfDesc(_vm->_global->_videoMode, width, height, flags);
- _vm->_video->clearSurf(*_spritesArray[index]);
+ _spritesArray[index]->clear();
}
void Draw::adjustCoords(char adjust, int16 *coord1, int16 *coord2) {
@@ -381,14 +381,14 @@ int Draw::stringLength(const char *str, int16 fontIndex) {
}
void Draw::drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2,
- int16 transp, SurfaceDesc &dest, const Font &font) {
+ int16 transp, Surface &dest, const Font &font) {
while (*str != '\0') {
const int16 charRight = x + font.getCharWidth(*str);
const int16 charBottom = y + font.getCharHeight();
if ((charRight <= dest.getWidth()) && (charBottom <= dest.getHeight()))
- _vm->_video->drawLetter(*str, x, y, font, transp, color1, color2, dest);
+ font.drawLetter(dest, *str, x, y, color1, color2, transp);
x += font.getCharWidth(*str);
str++;
@@ -530,777 +530,6 @@ void Draw::oPlaytoons_sub_F_1B(uint16 id, int16 left, int16 top, int16 right, in
return;
}
-void Draw::activeWin(int16 id) {
- bool found = false;
- int16 t[10], t2[10];
- int nextId = -1;
- int oldId = -1;
- SurfaceDescPtr tempSrf;
- SurfaceDescPtr oldSrf[10];
-
- if (_fascinWin[id].id == -1)
- return;
-
- blitInvalidated();
-
- for (int i = 0; i < 10; i++) {
- t[i] = -1;
- t2[i] = -1;
- oldSrf[i].reset();
- }
-
- for (int i = 0; i < 10; i++)
- if ((i != id) && (_fascinWin[i].id > _fascinWin[id].id) && (winOverlap(i, id))) {
- t[_fascinWin[i].id] = i;
- found = true;
- }
-
- if (found) {
- for (int i = 9; i >= 0; i--) {
- if (t[i] != -1) {
- if (nextId != -1)
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
- _fascinWin[t[i]].left, _fascinWin[t[i]].top,
- _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
- _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
- _fascinWin[t[i]].left & 7, 0, 0);
- t2[i] = nextId;
- restoreWin(t[i]);
- nextId = t[i];
- }
- }
-
- oldId = nextId;
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
- _fascinWin[id].left, _fascinWin[id].top,
- _fascinWin[id].left + _fascinWin[id].width - 1,
- _fascinWin[id].top + _fascinWin[id].height - 1,
- _fascinWin[id].left & 7, 0, 0);
- restoreWin(id);
- nextId = id;
-
- for (int i = 0; i < 10; i++) {
- if (t[i] != -1) {
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[nextId].savedSurface,
- _fascinWin[t[i]].left, _fascinWin[t[i]].top,
- _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
- _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
- _fascinWin[t[i]].left & 7, 0, 0);
- oldSrf[t[i]] = _fascinWin[nextId].savedSurface;
- if (t2[i] != -1)
- _vm->_video->drawSprite(*_fascinWin[t2[i]].savedSurface, *_backSurface,
- _fascinWin[t[i]].left & 7, 0,
- (_fascinWin[t[i]].left & 7) + _fascinWin[t[i]].width - 1,
- _fascinWin[t[i]].height - 1, _fascinWin[t[i]].left,
- _fascinWin[t[i]].top, 0);
- else {
- // Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *_backSurface,
- _fascinWin[t[i]].left, _fascinWin[t[i]].top,
- _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
- _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
- _fascinWin[t[i]].left, _fascinWin[t[i]].top, 0);
- }
- invalidateRect(_fascinWin[t[i]].left, _fascinWin[t[i]].top,
- _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
- _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1);
- nextId = t2[i];
- }
- }
-
- tempSrf = _vm->_video->initSurfDesc(_vm->_global->_videoMode, _winMaxWidth + 7, _winMaxHeight, 0);
- _vm->_video->drawSprite(*_backSurface, *tempSrf,
- _fascinWin[id].left, _fascinWin[id].top,
- _fascinWin[id].left + _fascinWin[id].width - 1,
- _fascinWin[id].top + _fascinWin[id].height - 1,
- _fascinWin[id].left & 7, 0, 0);
- _vm->_video->drawSprite(*_fascinWin[oldId].savedSurface, *_backSurface,
- _fascinWin[id].left & 7, 0,
- (_fascinWin[id].left & 7) + _fascinWin[id].width - 1,
- _fascinWin[id].height - 1,
- _fascinWin[id].left, _fascinWin[id].top, 0);
-
- _fascinWin[oldId].savedSurface.reset();
- _fascinWin[oldId].savedSurface = tempSrf;
- oldSrf[id] = _fascinWin[oldId].savedSurface;
-
- invalidateRect(_fascinWin[id].left, _fascinWin[id].top,
- _fascinWin[id].left + _fascinWin[id].width - 1,
- _fascinWin[id].top + _fascinWin[id].height - 1);
- nextId = id;
-
- for (int j = 0; j < 10; j++) {
- if (oldSrf[j]!=0)
- _fascinWin[j].savedSurface = oldSrf[j];
- }
- }
-
- for (int i = 0; i < 10; i++) {
- if ((i != id) && (_fascinWin[i].id > _fascinWin[id].id))
- _fascinWin[i].id--;
- }
-
- _fascinWin[id].id = _winCount - 1;
-}
-
-bool Draw::winOverlap(int16 idWin1, int16 idWin2) {
- if ((_fascinWin[idWin1].left + _fascinWin[idWin1].width <= _fascinWin[idWin2].left) ||
- (_fascinWin[idWin2].left + _fascinWin[idWin2].width <= _fascinWin[idWin1].left) ||
- (_fascinWin[idWin1].top + _fascinWin[idWin1].height <= _fascinWin[idWin2].top ) ||
- (_fascinWin[idWin2].top + _fascinWin[idWin2].height <= _fascinWin[idWin1].top ))
- return false;
-
- return true;
-}
-
-void Draw::closeWin(int16 i) {
- if (_fascinWin[i].id == -1)
- return;
-
- WRITE_VAR((_winVarArrayStatus / 4) + i, VAR((_winVarArrayStatus / 4) + i) | 1);
- restoreWin(i);
- _fascinWin[i].id = -1;
- _fascinWin[i].savedSurface.reset();
- _winCount--;
-}
-
-void Draw::closeAllWin() {
- for (int i = 0; i < 10; i++){
- activeWin(i);
- closeWin(i);
- }
-}
-
-int16 Draw::openWin(int16 id) {
- if (_fascinWin[id].id != -1)
- return 0;
-
- _fascinWin[id].id = _winCount++;
- _fascinWin[id].left = VAR((_winVarArrayLeft / 4) + id);
- _fascinWin[id].top = VAR((_winVarArrayTop / 4) + id);
- _fascinWin[id].width = VAR((_winVarArrayWidth / 4) + id);
- _fascinWin[id].height = VAR((_winVarArrayHeight / 4) + id);
-
- _fascinWin[id].savedSurface = _vm->_video->initSurfDesc(_vm->_global->_videoMode, _winMaxWidth + 7, _winMaxHeight, 0);
-
- saveWin(id);
- WRITE_VAR((_winVarArrayStatus / 4) + id, VAR((_winVarArrayStatus / 4) + id) & 0xFFFFFFFE);
-
- return 1;
-}
-
-void Draw::restoreWin(int16 i) {
- _vm->_video->drawSprite(*_fascinWin[i].savedSurface, *_backSurface,
- _fascinWin[i].left & 7, 0,
- (_fascinWin[i].left & 7) + _fascinWin[i].width - 1, _fascinWin[i].height - 1,
- _fascinWin[i].left, _fascinWin[i].top, 0);
- invalidateRect(_fascinWin[i].left, _fascinWin[i].top,
- _fascinWin[i].left + _fascinWin[i].width - 1,
- _fascinWin[i].top + _fascinWin[i].height - 1);
-}
-
-void Draw::saveWin(int16 id) {
- _vm->_video->drawSprite(*_backSurface, *_fascinWin[id].savedSurface,
- _fascinWin[id].left, _fascinWin[id].top,
- _fascinWin[id].left + _fascinWin[id].width - 1,
- _fascinWin[id].top + _fascinWin[id].height - 1,
- _fascinWin[id].left & 7, 0, 0);
-}
-
-void Draw::winMove(int16 id) {
- int oldLeft = _fascinWin[id].left;
- int oldTop = _fascinWin[id].top;
-
- restoreWin(id);
-
- _fascinWin[id].left = _vm->_global->_inter_mouseX;
- _fascinWin[id].top = _vm->_global->_inter_mouseY;
-
- WRITE_VAR((_winVarArrayLeft / 4) + id, _fascinWin[id].left);
- WRITE_VAR((_winVarArrayTop / 4) + id, _fascinWin[id].top);
-
- saveWin(id);
-
- // Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *_backSurface,
- oldLeft, oldTop,
- oldLeft + _fascinWin[id].width - 1,
- oldTop + _fascinWin[id].height - 1,
- _fascinWin[id].left, _fascinWin[id].top, 0);
- invalidateRect(_fascinWin[id].left, _fascinWin[id].top,
- _fascinWin[id].left + _fascinWin[id].width - 1,
- _fascinWin[id].top + _fascinWin[id].height - 1);
-}
-
-void Draw::winTrace(int16 left, int16 top, int16 width, int16 height) {
- // TODO: Implement proper behavior for the trace of the Window. In short,
- // - drawline currently use the wrong surface, to be fixed
- // - dirtiedRect should be put after the 4 drawlines when the surface is fixed <- Not in 256 col version
- // - drawline should be replaced by a drawline with palette inversion
- // - Shift skipped as always set to zero (?)
-
- int16 right, bottom;
-
- right = left + width - 1;
- bottom = top + height - 1;
-
-// To be fixed : either wrong surface, or anything else, but crappy look.
-// _vm->_video->drawLine(*_frontSurface, left, top, right, top, 0);
-// _vm->_video->drawLine(*_frontSurface, left, top, left, bottom, 0);
-// _vm->_video->drawLine(*_frontSurface, left, bottom, right, bottom, 0);
-// _vm->_video->drawLine(*_frontSurface, right, top, right, bottom, 0);
-
-// Not in 256 col version
- _vm->_video->dirtyRectsAll();
-
-}
-
-void Draw::handleWinBorder(int16 id) {
- int16 minX = 0;
- int16 maxX = 320;
- int16 minY = 0;
- int16 maxY = 200;
-
- if (VAR((_winVarArrayStatus / 4) + id) & 8)
- minX = (int16)(VAR((_winVarArrayLimitsX / 4) + id) >> 16L);
- if (VAR((_winVarArrayStatus / 4) + id) & 16)
- maxX = (int16)(VAR((_winVarArrayLimitsX / 4) + id) & 0xFFFFL);
- if (VAR((_winVarArrayStatus / 4) + id) & 32)
- minY = (int16)(VAR((_winVarArrayLimitsY / 4) + id) >> 16L);
- if (VAR((_winVarArrayStatus / 4) + id) & 64)
- maxY = (int16)(VAR((_winVarArrayLimitsY / 4) + id) & 0xFFFFL);
-
- _vm->_global->_inter_mouseX = _fascinWin[id].left;
- _vm->_global->_inter_mouseY = _fascinWin[id].top;
-
- if (_vm->_global->_mousePresent)
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
-
- winTrace(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY, _fascinWin[id].width, _fascinWin[id].height);
- _cursorX = _vm->_global->_inter_mouseX;
- _cursorY = _vm->_global->_inter_mouseY;
-
- do {
- _vm->_game->checkKeys(&_vm->_global->_inter_mouseX, &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, 1);
-
- if (_vm->_global->_inter_mouseX != _cursorX || _vm->_global->_inter_mouseY != _cursorY) {
- if (_vm->_global->_inter_mouseX < minX) {
- _vm->_global->_inter_mouseX = minX;
- if (_vm->_global->_mousePresent)
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
- }
-
- if (_vm->_global->_inter_mouseY < minY) {
- _vm->_global->_inter_mouseY = minY;
- if (_vm->_global->_mousePresent)
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
- }
-
- if (_vm->_global->_inter_mouseX + _fascinWin[id].width > maxX) {
- _vm->_global->_inter_mouseX = maxX - _fascinWin[id].width;
- if (_vm->_global->_mousePresent)
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
- }
-
- if (_vm->_global->_inter_mouseY + _fascinWin[id].height > maxY) {
- _vm->_global->_inter_mouseY = maxY - _fascinWin[id].height;
- if (_vm->_global->_mousePresent)
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
- }
-
- winTrace(_cursorX, _cursorY, _fascinWin[id].width, _fascinWin[id].height);
- winTrace(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY, _fascinWin[id].width, _fascinWin[id].height);
- _cursorX = _vm->_global->_inter_mouseX;
- _cursorY = _vm->_global->_inter_mouseY;
- }
- } while (_vm->_game->_mouseButtons);
- winTrace(_cursorX, _cursorY, _fascinWin[id].width, _fascinWin[id].height);
- _cursorX = _vm->_global->_inter_mouseX;
- _cursorY = _vm->_global->_inter_mouseY;
-}
-
-int16 Draw::handleCurWin() {
- int8 matchNum = 0;
- int16 bestMatch = -1;
-
- if ((_vm->_game->_mouseButtons != 1) || ((_renderFlags & 128) == 0))
- return 0;
-
- for (int i = 0; i < 10; i++)
- if (_fascinWin[i].id != -1) {
- if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left) &&
- (_vm->_global->_inter_mouseX < _fascinWin[i].left + _fascinWin[i].width) &&
- (_vm->_global->_inter_mouseY >= _fascinWin[i].top) &&
- (_vm->_global->_inter_mouseY < _fascinWin[i].top + _fascinWin[i].height)) {
-
- if (_fascinWin[i].id == _winCount - 1) {
- if ((_vm->_global->_inter_mouseX < _fascinWin[i].left + 12) &&
- (_vm->_global->_inter_mouseY < _fascinWin[i].top + 12) &&
- (VAR(_winVarArrayStatus / 4 + i) & 2)) {
-
- blitCursor();
- activeWin(i);
- closeWin(i);
- _vm->_util->waitMouseRelease(1);
- return i;
- }
-
- if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left + _fascinWin[i].width - 12) &&
- (_vm->_global->_inter_mouseY < _fascinWin[i].top + 12) &&
- (VAR(_winVarArrayStatus / 4 + i) & 4) &&
- (_vm->_global->_mousePresent) &&
- (_vm->_global->_videoMode != 0x07)) {
-
- blitCursor();
- handleWinBorder(i);
- winMove(i);
- _vm->_global->_inter_mouseX = _fascinWin[i].left + _fascinWin[i].width - 11;
- _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
- return -i;
- }
-
- return 0;
-
- } else
- if (_fascinWin[i].id > bestMatch) {
- bestMatch = _fascinWin[i].id;
- matchNum = i;
- }
- }
- }
- if (bestMatch != -1) {
- warning("handleCurWin - activeWin %d", matchNum);
- blitCursor();
- activeWin(matchNum);
- }
-
- return 0;
-}
-
-void Draw::winDecomp(int16 x, int16 y, SurfaceDescPtr destPtr) {
- Resource *resource;
- resource = _vm->_game->_resources->getResource((uint16) _spriteLeft,
- &_spriteRight, &_spriteBottom);
-
- if (!resource)
- return;
-
- _vm->_video->drawPackedSprite(resource->getData(),
- _spriteRight, _spriteBottom, x, y, _transparency, *destPtr);
-
- delete resource;
- return;
-}
-
-void Draw::winDraw(int16 fct) {
- int16 left;
- int16 top;
- int16 width;
- int16 height;
-
- bool found = false;
- int len;
- Resource *resource;
- int table[10];
- SurfaceDescPtr tempSrf;
-
- if (_destSurface == kBackSurface) {
-
- if (_vm->_global->_curWinId) {
- if (_fascinWin[_vm->_global->_curWinId].id == -1)
- return;
-
- else {
- _destSpriteX += _fascinWin[_vm->_global->_curWinId].left;
- _destSpriteY += _fascinWin[_vm->_global->_curWinId].top;
- if (fct == 3 || (fct >= 7 && fct <= 9)) {
- _spriteRight += _fascinWin[_vm->_global->_curWinId].left;
- _spriteBottom += _fascinWin[_vm->_global->_curWinId].top;
- }
- }
- }
-
- left = _destSpriteX;
- top = _destSpriteY;
-
- } else {
- if (_vm->_global->_curWinId) {
- if (_fascinWin[_vm->_global->_curWinId].id == -1)
- return;
- else {
- _spriteLeft += _fascinWin[_vm->_global->_curWinId].left;
- _spriteTop += _fascinWin[_vm->_global->_curWinId].top;
- }
- }
-
- left = _spriteLeft;
- top = _spriteTop;
- }
-
- for (int i = 0; i < 10; i++)
- table[i] = 0;
-
- switch (fct) {
- case DRAW_BLITSURF: // 0 - move
- case DRAW_FILLRECT: // 2 - fill rectangle
- width = left + _spriteRight - 1;
- height = top + _spriteBottom - 1;
- break;
-
- case DRAW_PUTPIXEL: // 1 - put a pixel
- width = _destSpriteX;
- height = _destSpriteY;
- break;
-
- case DRAW_DRAWLINE: // 3 - draw line
- case DRAW_DRAWBAR: // 7 - draw border
- case DRAW_CLEARRECT: // 8 - clear rectangle
- case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
- width = _spriteRight;
- height = _spriteBottom;
- break;
-
- case DRAW_INVALIDATE: // 4 - Draw a circle
- left = _destSpriteX - _spriteRight;
- top = _destSpriteY - _spriteRight;
- width = _destSpriteX + _spriteRight;
- height = _destSpriteY + _spriteBottom;
- break;
-
- case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
- // TODO: check the implementation, currently dirty cut and paste of DRAW_SPRITE code
- resource = _vm->_game->_resources->getResource((_spriteLeft & 0x3FFF),
- &_spriteRight, &_spriteBottom);
-
- if (!resource) {
- width = 0;
- height = 0;
- break;
- }
-
- _vm->_video->drawPackedSprite(resource->getData(),
- _spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
- _transparency, *_spritesArray[_destSurface]);
-
- dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
- _destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
-
- delete resource;
-
- width = _destSpriteX + _spriteRight - 1;
- height = _destSpriteY + _spriteBottom - 1;
- break;
-
- case DRAW_PRINTTEXT: // 6 - Display string
- width = _destSpriteX - 1 + strlen(_textToPrint) * _fonts[_fontIndex]->getCharWidth();
- height = _destSpriteY - 1 + _fonts[_fontIndex]->getCharHeight();
- break;
-
- case DRAW_DRAWLETTER: // 10 - Display a character
- if (_fontToSprite[_fontIndex].sprite == -1) {
- width = _destSpriteX - 1 + _fonts[_fontIndex]->getCharWidth();
- height = _destSpriteY - 1 + _fonts[_fontIndex]->getCharHeight();
- } else {
- width = _destSpriteX + _fontToSprite[_fontIndex].width - 1;
- height = _destSpriteY + _fontToSprite[_fontIndex].height - 1;
- }
- break;
-
- default:
- width = 0;
- height = 0;
- warning("Unexpected fct value %d", fct);
- break;
- }
-
- for (int i = 0; i < 10; i++)
- if ((i != _vm->_global->_curWinId) && (_fascinWin[i].id != -1))
- if (!_vm->_global->_curWinId || _fascinWin[i].id>_fascinWin[_vm->_global->_curWinId].id)
- if ((_fascinWin[i].left + _fascinWin[i].width > left) && (width >= _fascinWin[i].left) &&
- (_fascinWin[i].top + _fascinWin[i].height > top ) && (height >= _fascinWin[i].top)) {
- found = true;
- table[_fascinWin[i].id] = i;
- }
-
- if ((_sourceSurface == kBackSurface) && (fct == 0)) {
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *_spritesArray[_destSurface],
- _spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
- _spriteTop + _spriteBottom - 1, _destSpriteX, _destSpriteY, _transparency);
- if (!found)
- return;
-
- int j = 0;
- if (_vm->_global->_curWinId != 0)
- j = _fascinWin[_vm->_global->_curWinId].id + 1;
-
- for (int i = 9; i >= j; i--) {
- if (table[i])
- _vm->_video->drawSprite(*_fascinWin[table[i]].savedSurface, *_spritesArray[_destSurface],
- _fascinWin[table[i]].left & 7, 0,
- (_fascinWin[table[i]].left & 7) + _fascinWin[table[i]].width - 1,
- _fascinWin[table[i]].height - 1, _fascinWin[table[i]].left - _spriteLeft + _destSpriteX,
- _fascinWin[table[i]].top - _spriteTop + _destSpriteY, 0);
- }
- return;
- }
-
- if (found) {
- tempSrf = _vm->_video->initSurfDesc(_vm->_global->_videoMode, width - left + 1, height - top + 1, 0);
- _vm->_video->drawSprite(*_backSurface, *tempSrf, left, top, width, height, 0, 0, 0);
-
- int max = 0;
- if (_vm->_global->_curWinId != 0)
- max = _fascinWin[_vm->_global->_curWinId].id + 1;
-
- for (int i = 9; i >= max; i--) {
- if (table[i])
- _vm->_video->drawSprite(*_fascinWin[table[i]].savedSurface, *tempSrf,
- _fascinWin[table[i]].left & 7, 0,
- (_fascinWin[table[i]].left & 7) + _fascinWin[table[i]].width - 1,
- _fascinWin[table[i]].height - 1,
- _fascinWin[table[i]].left - left,
- _fascinWin[table[i]].top - top , 0);
- }
-
- invalidateRect(left, top, width, height);
-
- switch (fct) {
- case DRAW_BLITSURF: // 0 - move
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *tempSrf,
- _spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
- _spriteTop + _spriteBottom - 1, 0, 0, _transparency);
- break;
-
- case DRAW_PUTPIXEL: // 1 - put a pixel
- _vm->_video->putPixel(0, 0, _frontColor, *tempSrf);
- break;
-
- case DRAW_FILLRECT: // 2 - fill rectangle
- _vm->_video->fillRect(*tempSrf, 0, 0, _spriteRight - 1, _spriteBottom - 1, _backColor);
- break;
-
- case DRAW_DRAWLINE: // 3 - draw line
- _vm->_video->drawLine(*tempSrf, 0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
- break;
-
- case DRAW_INVALIDATE: // 4 - Draw a circle
- _vm->_video->drawCircle(*tempSrf, _spriteRight, _spriteRight, _spriteRight, _frontColor);
- break;
-
- case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
- winDecomp(0, 0, tempSrf);
- break;
-
- case DRAW_PRINTTEXT: // 6 - Display string
- len = strlen(_textToPrint);
- for (int j = 0; j < len; j++)
- _vm->_video->drawLetter(_textToPrint[j], j * _fonts[_fontIndex]->getCharWidth(), 0,
- *_fonts[_fontIndex], _transparency, _frontColor, _backColor, *tempSrf);
- _destSpriteX += len * _fonts[_fontIndex]->getCharWidth();
- break;
-
- case DRAW_DRAWBAR: // 7 - draw border
- _vm->_video->drawLine(*tempSrf, 0, _spriteBottom - _destSpriteY, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
- _vm->_video->drawLine(*tempSrf, 0, 0, 0, _spriteBottom - _destSpriteY, _frontColor);
- _vm->_video->drawLine(*tempSrf, _spriteRight - _destSpriteX, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
- _vm->_video->drawLine(*tempSrf, 0, 0, _spriteRight - _destSpriteX, 0, _frontColor);
- break;
-
- case DRAW_CLEARRECT: // 8 - clear rectangle
- if (_backColor < 16)
- _vm->_video->fillRect(*tempSrf, 0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _backColor);
- break;
-
- case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
- _vm->_video->fillRect(*tempSrf, 0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _backColor);
- break;
-
- case DRAW_DRAWLETTER: // 10 - Display a character
- if (_fontToSprite[_fontIndex].sprite == -1) {
-
- if (_letterToPrint)
- _vm->_video->drawLetter(_letterToPrint, 0, 0, *_fonts[_fontIndex], _transparency, _frontColor, _backColor, *tempSrf);
- } else {
- int xx, yy, nn;
- nn = _spritesArray[_fontToSprite[_fontIndex].sprite]->getWidth() / _fontToSprite[_fontIndex].width;
- yy = ((_letterToPrint - _fontToSprite[_fontIndex].base) / nn) * _fontToSprite[_fontIndex].height;
- xx = ((_letterToPrint - _fontToSprite[_fontIndex].base) % nn) * _fontToSprite[_fontIndex].width;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite], *tempSrf,
- xx, yy, xx + _fontToSprite[_fontIndex].width - 1,
- yy + _fontToSprite[_fontIndex].height - 1, 0, 0, _transparency);
- }
- break;
-
- default:
- warning("Unexpected fct value");
- break;
- }
-
- int i = 0;
- if (_vm->_global->_curWinId != 0)
- i = _fascinWin[_vm->_global->_curWinId].id + 1;
-
- for (; i < 10; i++) {
- if (table[i]) {
- int k = table[i];
- _vm->_video->drawSprite(*tempSrf, *_fascinWin[k].savedSurface,
- 0, 0, width - left, height - top,
- left - _fascinWin[k].left + (_fascinWin[k].left & 7),
- top - _fascinWin[k].top, 0);
- // Shift skipped as always set to zero (?)
- _vm->_video->drawSprite(*_frontSurface, *tempSrf,
- MAX(left , _fascinWin[k].left),
- MAX(top , _fascinWin[k].top),
- MIN(width , (int16) (_fascinWin[k].left + _fascinWin[k].width - 1)),
- MIN(height, (int16) (_fascinWin[k].top + _fascinWin[k].height - 1)),
- MAX(left , _fascinWin[k].left) - left,
- MAX(top , _fascinWin[k].top) - top, 0);
- if (_cursorIndex != -1)
- _vm->_video->drawSprite(*_cursorSpritesBack, *tempSrf,
- 0, 0, _cursorWidth - 1, _cursorHeight - 1,
- _cursorX - left, _cursorY - top, 0);
- for (int j = 9; j > i; j--) {
- if (table[j] && winOverlap(k, table[j])) {
- int l = table[j];
- _vm->_video->drawSprite(*_fascinWin[l].savedSurface, *tempSrf,
- MAX(_fascinWin[l].left, _fascinWin[k].left)
- - _fascinWin[l].left + (_fascinWin[l].left & 7),
- MAX(_fascinWin[l].top , _fascinWin[k].top ) - _fascinWin[l].top,
- MIN(_fascinWin[l].left + _fascinWin[l].width - 1, _fascinWin[k].left + _fascinWin[k].width - 1)
- - _fascinWin[l].left + (_fascinWin[l].left & 7),
- MIN(_fascinWin[l].top + _fascinWin[l].height - 1, _fascinWin[k].top + _fascinWin[k].height - 1)
- - _fascinWin[l].top,
- MAX(_fascinWin[l].left, _fascinWin[k].left) - left,
- MAX(_fascinWin[l].top , _fascinWin[k].top ) - top, 0);
- }
- }
- }
- }
- _vm->_video->drawSprite(*tempSrf, *_backSurface, 0, 0, width - left, height - top, left, top, 0);
- tempSrf.reset();
- } else {
- invalidateRect(left, top, width, height);
- switch (fct) {
- case DRAW_BLITSURF: // 0 - move
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface], *_backSurface,
- _spriteLeft, _spriteTop,
- _spriteLeft + _spriteRight - 1,
- _spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
- break;
- case DRAW_PUTPIXEL: // 1 - put a pixel
- _vm->_video->putPixel(_destSpriteX, _destSpriteY, _frontColor, *_backSurface);
- break;
-
- case DRAW_FILLRECT: // 2 - fill rectangle
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1, _backColor);
- break;
-
- case DRAW_DRAWLINE: // 3 - draw line
- _vm->_video->drawLine(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _frontColor);
- break;
-
- case DRAW_INVALIDATE: // 4 - Draw a circle
- _vm->_video->drawCircle(*_backSurface, _spriteRight, _spriteRight, _spriteRight, _frontColor);
- break;
-
- case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
- winDecomp(_destSpriteX, _destSpriteY, _backSurface);
- break;
-
- case DRAW_PRINTTEXT: // 6 - Display string
- len = strlen(_textToPrint);
- for (int j = 0; j < len; j++)
- _vm->_video->drawLetter(_textToPrint[j], _destSpriteX + j * _fonts[_fontIndex]->getCharWidth(),
- _destSpriteY, *_fonts[_fontIndex], _transparency, _frontColor, _backColor, *_backSurface);
- _destSpriteX += len * _fonts[_fontIndex]->getCharWidth();
- break;
-
- case DRAW_DRAWBAR: // 7 - draw border
- _vm->_video->drawLine(*_backSurface, _destSpriteX, _spriteBottom, _spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_backSurface, _destSpriteX, _destSpriteY, _destSpriteX, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_backSurface, _spriteRight, _destSpriteY, _spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _destSpriteY, _frontColor);
- break;
-
- case DRAW_CLEARRECT: // 8 - clear rectangle
- if (_backColor < 16)
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
- break;
-
- case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
- _vm->_video->fillRect(*_backSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
- break;
-
- case DRAW_DRAWLETTER: // 10 - Display a character
- if (_fontToSprite[_fontIndex].sprite == -1) {
- if (_letterToPrint)
- _vm->_video->drawLetter(_letterToPrint, _destSpriteX, _destSpriteY, *_fonts[_fontIndex], _transparency,
- _frontColor, _backColor, *_spritesArray[_destSurface]);
- } else {
- int xx, yy, nn;
- nn = _spritesArray[_fontToSprite[_fontIndex].sprite]->getWidth() / _fontToSprite[_fontIndex].width;
- yy = ((_letterToPrint - _fontToSprite[_fontIndex].base) / nn) * _fontToSprite[_fontIndex].height;
- xx = ((_letterToPrint - _fontToSprite[_fontIndex].base) % nn) * _fontToSprite[_fontIndex].width;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite], *_spritesArray[_destSurface],
- xx, yy,
- xx + _fontToSprite[_fontIndex].width - 1,
- yy + _fontToSprite[_fontIndex].height - 1,
- _destSpriteX, _destSpriteY, _transparency);
- }
- break;
-
- default:
- warning("Unexpected fct value");
- break;
- }
- }
-
- if (_renderFlags & 16) {
- if (_sourceSurface == kBackSurface) {
- _spriteLeft -= _backDeltaX;
- _spriteTop -= _backDeltaY;
- }
- if (_destSurface == kBackSurface) {
- _destSpriteX -= _backDeltaX;
- _destSpriteY -= _backDeltaY;
- }
- }
-
- if (_vm->_global->_curWinId) {
- _destSpriteX -= _fascinWin[_vm->_global->_curWinId].left;
- _destSpriteY -= _fascinWin[_vm->_global->_curWinId].top;
- }
-}
-
-int16 Draw::isOverWin(int16 &dx, int16 &dy) {
- int16 bestMatch = -1;
-
- if ((_renderFlags & 128) == 0)
- return -1;
-
- for (int i = 0; i < 10; i++)
- if (_fascinWin[i].id != -1) {
- if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left) &&
- (_vm->_global->_inter_mouseX < _fascinWin[i].left + _fascinWin[i].width) &&
- (_vm->_global->_inter_mouseY >= _fascinWin[i].top) &&
- (_vm->_global->_inter_mouseY < _fascinWin[i].top + _fascinWin[i].height)) {
-
- if (_fascinWin[i].id == _winCount - 1) {
- dx = _fascinWin[i].left;
- dy = _fascinWin[i].top;
- return(i);
- } else
- if (_fascinWin[i].id > bestMatch)
- bestMatch = _fascinWin[i].id;
- }
- }
-
- if (bestMatch != -1)
- return(0);
- else
- return(-1);
-}
int32 Draw::getSpriteRectSize(int16 index) {
if (!_spritesArray[index])
return 0;
@@ -1319,14 +548,10 @@ void Draw::forceBlit(bool backwards) {
return;
if (!backwards) {
- _vm->_video->drawSprite(*_backSurface, *_frontSurface, 0, 0,
- _backSurface->getWidth() - 1, _backSurface->getHeight() - 1,
- 0, 0, 0);
+ _frontSurface->blit(*_backSurface);
_vm->_video->dirtyRectsAll();
} else
- _vm->_video->drawSprite(*_frontSurface, *_backSurface, 0, 0,
- _frontSurface->getWidth() - 1, _frontSurface->getHeight() - 1,
- 0, 0, 0);
+ _backSurface->blit(*_frontSurface);
}
@@ -1373,7 +598,7 @@ const int16 Draw::_wobbleTable[360] = {
-0x0A03, -0x08E8, -0x07CC, -0x06B0, -0x0593, -0x0476, -0x0359, -0x023B, -0x011D
};
-void Draw::wobble(SurfaceDesc &surfDesc) {
+void Draw::wobble(Surface &surfDesc) {
int16 amplitude = 32;
uint16 curFrame = 0;
uint16 frameWobble = 0;
@@ -1397,16 +622,14 @@ void Draw::wobble(SurfaceDesc &surfDesc) {
amplitude--;
for (uint16 y = 0; y < _vm->_height; y++)
- _vm->_video->drawSprite(surfDesc, *_frontSurface,
- 0, y, _vm->_width - 1, y, offsets[y], y, 0);
+ _frontSurface->blit(surfDesc, 0, y, _vm->_width - 1, y, offsets[y], y);
_vm->_palAnim->fadeStep(0);
_vm->_video->dirtyRectsAll();
_vm->_video->waitRetrace();
}
- _vm->_video->drawSprite(surfDesc, *_frontSurface,
- 0, 0, _vm->_width - 1, _vm->_height - 1, 0, 0, 0);
+ _frontSurface->blit(surfDesc);
_applyPal = false;
_invalidatedCount = 0;
diff --git a/engines/gob/draw.h b/engines/gob/draw.h
index fa3cbb84cc..fe37382666 100644
--- a/engines/gob/draw.h
+++ b/engines/gob/draw.h
@@ -66,7 +66,7 @@ public:
int16 top;
int16 width;
int16 height;
- SurfaceDescPtr savedSurface;
+ SurfacePtr savedSurface;
};
int16 _renderFlags;
@@ -98,7 +98,7 @@ public:
FontToSprite _fontToSprite[4];
Font *_fonts[kFontCount];
- Common::Array<SurfaceDescPtr> _spritesArray;
+ Common::Array<SurfacePtr> _spritesArray;
int16 _invalidatedCount;
int16 _invalidatedLefts[30];
@@ -112,8 +112,8 @@ public:
bool _paletteCleared;
bool _applyPal;
- SurfaceDescPtr _backSurface;
- SurfaceDescPtr _frontSurface;
+ SurfacePtr _backSurface;
+ SurfacePtr _frontSurface;
int16 _unusedPalette1[18];
int16 _unusedPalette2[16];
@@ -137,9 +137,9 @@ public:
int32 _cursorHotspotXVar;
int32 _cursorHotspotYVar;
- SurfaceDescPtr _cursorSprites;
- SurfaceDescPtr _cursorSpritesBack;
- SurfaceDescPtr _scummvmCursor;
+ SurfacePtr _cursorSprites;
+ SurfacePtr _cursorSpritesBack;
+ SurfacePtr _scummvmCursor;
int16 _cursorAnim;
int8 _cursorAnimLow[40];
@@ -174,7 +174,7 @@ public:
void clearPalette();
void dirtiedRect(int16 surface, int16 left, int16 top, int16 right, int16 bottom);
- void dirtiedRect(SurfaceDescPtr surface, int16 left, int16 top, int16 right, int16 bottom);
+ void dirtiedRect(SurfacePtr surface, int16 left, int16 top, int16 right, int16 bottom);
void initSpriteSurf(int16 index, int16 width, int16 height, int16 flags);
void freeSprite(int16 index) {
@@ -187,31 +187,16 @@ public:
}
int stringLength(const char *str, int16 fontIndex);
void drawString(const char *str, int16 x, int16 y, int16 color1, int16 color2,
- int16 transp, SurfaceDesc &dest, const Font &font);
+ int16 transp, Surface &dest, const Font &font);
void printTextCentered(int16 id, int16 left, int16 top, int16 right,
int16 bottom, const char *str, int16 fontIndex, int16 color);
void oPlaytoons_sub_F_1B( uint16 id, int16 left, int16 top, int16 right, int16 bottom, char *paramStr, int16 var3, int16 var4, int16 shortId);
- int16 openWin(int16 id);
- int16 handleCurWin();
- bool winOverlap(int16 idWin1, int16 idWin2);
- void winDecomp(int16 x, int16 y, SurfaceDescPtr destPtr);
- void activeWin(int16 id);
- void closeWin(int16 id);
- void closeAllWin();
- void restoreWin(int16 i);
- void saveWin(int16 id);
- void winMove(int16 id);
- void handleWinBorder(int16 id);
- void winDraw(int16 fct);
- void winTrace(int16 left, int16 top, int16 width, int16 height);
- int16 isOverWin(int16 &dx, int16 &dy);
-
int32 getSpriteRectSize(int16 index);
void forceBlit(bool backwards = false);
static const int16 _wobbleTable[360];
- void wobble(SurfaceDesc &surfDesc);
+ void wobble(Surface &surfDesc);
Font *loadFont(const char *path) const;
bool loadFont(int fontIndex, const char *path);
@@ -223,6 +208,15 @@ public:
virtual void printTotText(int16 id) = 0;
virtual void spriteOperation(int16 operation) = 0;
+ virtual int16 openWin(int16 id) { return 0; }
+ virtual void closeWin(int16 id) {}
+ virtual int16 handleCurWin() { return 0; }
+ virtual int16 getWinFromCoord(int16 &dx, int16 &dy) { return -1; }
+ virtual void moveWin(int16 id) {}
+ virtual bool overlapWin(int16 idWin1, int16 idWin2) { return false; }
+ virtual void closeAllWin() {}
+ virtual void activeWin(int16 id) {}
+
Draw(GobEngine *vm);
virtual ~Draw();
@@ -272,6 +266,23 @@ public:
Draw_Fascination(GobEngine *vm);
virtual ~Draw_Fascination() {}
virtual void spriteOperation(int16 operation);
+
+ void decompWin(int16 x, int16 y, SurfacePtr destPtr);
+ void drawWin(int16 fct);
+ void saveWin(int16 id);
+ void restoreWin(int16 id);
+ void handleWinBorder(int16 id);
+ void drawWinTrace(int16 left, int16 top, int16 width, int16 height);
+
+ virtual int16 openWin(int16 id);
+ virtual void closeWin(int16 id);
+ virtual int16 getWinFromCoord(int16 &dx, int16 &dy);
+ virtual int16 handleCurWin();
+ virtual void activeWin(int16 id);
+ virtual void moveWin(int16 id);
+ virtual bool overlapWin(int16 idWin1, int16 idWin2);
+ virtual void closeAllWin();
+
};
class Draw_Playtoons: public Draw_v2 {
diff --git a/engines/gob/draw_fascin.cpp b/engines/gob/draw_fascin.cpp
index 1e01db7dfb..86e5cb8314 100644
--- a/engines/gob/draw_fascin.cpp
+++ b/engines/gob/draw_fascin.cpp
@@ -28,6 +28,8 @@
#include "gob/draw.h"
#include "gob/game.h"
+#include "gob/global.h"
+#include "gob/inter.h"
#include "gob/resources.h"
namespace Gob {
@@ -38,7 +40,7 @@ Draw_Fascination::Draw_Fascination(GobEngine *vm) : Draw_v2(vm) {
void Draw_Fascination::spriteOperation(int16 operation) {
int16 len;
int16 x, y;
- SurfaceDescPtr sourceSurf, destSurf;
+ SurfacePtr sourceSurf, destSurf;
bool deltaVeto;
int16 left;
int16 ratio;
@@ -71,7 +73,7 @@ void Draw_Fascination::spriteOperation(int16 operation) {
if (_renderFlags & 0x20) {
if (_destSurface == kBackSurface || (operation == 0 && _sourceSurface == kBackSurface)) {
- winDraw(operation);
+ drawWin(operation);
return;
}
}
@@ -147,26 +149,24 @@ void Draw_Fascination::spriteOperation(int16 operation) {
if (!sourceSurf || !destSurf)
break;
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface],
- *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
_spriteLeft, spriteTop,
_spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
break;
case DRAW_PUTPIXEL:
- _vm->_video->putPixel(_destSpriteX, _destSpriteY, _frontColor,
- *_spritesArray[_destSurface]);
+ _spritesArray[_destSurface]->putPixel(_destSpriteX, _destSpriteY, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _destSpriteX, _destSpriteY);
break;
case DRAW_FILLRECT:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX,
+ _spritesArray[_destSurface]->fillRect(destSpriteX,
_destSpriteY, _destSpriteX + _spriteRight - 1,
_destSpriteY + _spriteBottom - 1, _backColor);
@@ -175,15 +175,14 @@ void Draw_Fascination::spriteOperation(int16 operation) {
break;
case DRAW_DRAWLINE:
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
break;
case DRAW_INVALIDATE:
- _vm->_video->drawCircle(*_spritesArray[_destSurface], _destSpriteX,
+ _spritesArray[_destSurface]->drawCircle(_destSpriteX,
_destSpriteY, _spriteRight, _frontColor);
dirtiedRect(_destSurface, _destSpriteX - _spriteRight, _destSpriteY - _spriteBottom,
@@ -225,9 +224,8 @@ void Draw_Fascination::spriteOperation(int16 operation) {
byte *dataBuf = _vm->_game->_resources->getTexts() + _textToPrint[1] + 1;
len = *dataBuf++;
for (int i = 0; i < len; i++, dataBuf += 2) {
- _vm->_video->drawLetter(READ_LE_UINT16(dataBuf), _destSpriteX,
- _destSpriteY, *font, _transparency, _frontColor,
- _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], READ_LE_UINT16(dataBuf),
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
}
} else {
drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor,
@@ -236,9 +234,8 @@ void Draw_Fascination::spriteOperation(int16 operation) {
}
} else {
for (int i = 0; i < len; i++) {
- _vm->_video->drawLetter(_textToPrint[i], _destSpriteX,
- _destSpriteY, *font, _transparency,
- _frontColor, _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], _textToPrint[i],
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
_destSpriteX += font->getCharWidth(_textToPrint[i]);
}
}
@@ -253,11 +250,10 @@ void Draw_Fascination::spriteOperation(int16 operation) {
* _fontToSprite[_fontIndex].height;
x = ((_textToPrint[i] - _fontToSprite[_fontIndex].base) % ratio)
* _fontToSprite[_fontIndex].width;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite],
- *_spritesArray[_destSurface], x, y,
+ _spritesArray[_destSurface]->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite], x, y,
x + _fontToSprite[_fontIndex].width - 1,
y + _fontToSprite[_fontIndex].height - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
_destSpriteX += _fontToSprite[_fontIndex].width;
}
}
@@ -268,36 +264,28 @@ void Draw_Fascination::spriteOperation(int16 operation) {
case DRAW_DRAWBAR:
if (_needAdjust != 2) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom - 1,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _spriteBottom - 1,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect( _destSpriteX, _destSpriteY,
_destSpriteX + 1, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _spriteRight - 1, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect( _spriteRight - 1, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect( _destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY + 1, _frontColor);
} else {
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _spriteBottom,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_destSpriteX, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _spriteRight, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_spriteRight, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY, _frontColor);
}
@@ -306,19 +294,14 @@ void Draw_Fascination::spriteOperation(int16 operation) {
case DRAW_CLEARRECT:
if ((_backColor != 16) && (_backColor != 144)) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
- _spriteRight, _spriteBottom,
- _backColor);
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
}
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
break;
case DRAW_FILLRECTABS:
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
- _spriteRight, _spriteBottom, _backColor);
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
break;
@@ -352,4 +335,783 @@ void Draw_Fascination::spriteOperation(int16 operation) {
}
}
+void Draw_Fascination::drawWin(int16 fct) {
+ int16 left;
+ int16 top;
+ int16 width;
+ int16 height;
+
+ bool found = false;
+ int len;
+ Resource *resource;
+ int table[10];
+ SurfacePtr tempSrf;
+
+ if (_destSurface == kBackSurface) {
+
+ if (_vm->_global->_curWinId) {
+ if (_fascinWin[_vm->_global->_curWinId].id == -1)
+ return;
+
+ else {
+ _destSpriteX += _fascinWin[_vm->_global->_curWinId].left;
+ _destSpriteY += _fascinWin[_vm->_global->_curWinId].top;
+ if (fct == 3 || (fct >= 7 && fct <= 9)) {
+ _spriteRight += _fascinWin[_vm->_global->_curWinId].left;
+ _spriteBottom += _fascinWin[_vm->_global->_curWinId].top;
+ }
+ }
+ }
+
+ left = _destSpriteX;
+ top = _destSpriteY;
+
+ } else {
+ if (_vm->_global->_curWinId) {
+ if (_fascinWin[_vm->_global->_curWinId].id == -1)
+ return;
+ else {
+ _spriteLeft += _fascinWin[_vm->_global->_curWinId].left;
+ _spriteTop += _fascinWin[_vm->_global->_curWinId].top;
+ }
+ }
+
+ left = _spriteLeft;
+ top = _spriteTop;
+ }
+
+ for (int i = 0; i < 10; i++)
+ table[i] = 0;
+
+ switch (fct) {
+ case DRAW_BLITSURF: // 0 - move
+ case DRAW_FILLRECT: // 2 - fill rectangle
+ width = left + _spriteRight - 1;
+ height = top + _spriteBottom - 1;
+ break;
+
+ case DRAW_PUTPIXEL: // 1 - put a pixel
+ width = _destSpriteX;
+ height = _destSpriteY;
+ break;
+
+ case DRAW_DRAWLINE: // 3 - draw line
+ case DRAW_DRAWBAR: // 7 - draw border
+ case DRAW_CLEARRECT: // 8 - clear rectangle
+ case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
+ width = _spriteRight;
+ height = _spriteBottom;
+ break;
+
+ case DRAW_INVALIDATE: // 4 - Draw a circle
+ left = _destSpriteX - _spriteRight;
+ top = _destSpriteY - _spriteRight;
+ width = _destSpriteX + _spriteRight;
+ height = _destSpriteY + _spriteBottom;
+ break;
+
+ case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
+ // TODO: check the implementation, currently dirty cut and paste of DRAW_SPRITE code
+ resource = _vm->_game->_resources->getResource((_spriteLeft & 0x3FFF),
+ &_spriteRight, &_spriteBottom);
+
+ if (!resource) {
+ width = 0;
+ height = 0;
+ break;
+ }
+
+ _vm->_video->drawPackedSprite(resource->getData(),
+ _spriteRight, _spriteBottom, _destSpriteX, _destSpriteY,
+ _transparency, *_spritesArray[_destSurface]);
+
+ dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
+ _destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
+
+ delete resource;
+
+ width = _destSpriteX + _spriteRight - 1;
+ height = _destSpriteY + _spriteBottom - 1;
+ break;
+
+ case DRAW_PRINTTEXT: // 6 - Display string
+ width = _destSpriteX - 1 + strlen(_textToPrint) * _fonts[_fontIndex]->getCharWidth();
+ height = _destSpriteY - 1 + _fonts[_fontIndex]->getCharHeight();
+ break;
+
+ case DRAW_DRAWLETTER: // 10 - Display a character
+ if (_fontToSprite[_fontIndex].sprite == -1) {
+ width = _destSpriteX - 1 + _fonts[_fontIndex]->getCharWidth();
+ height = _destSpriteY - 1 + _fonts[_fontIndex]->getCharHeight();
+ } else {
+ width = _destSpriteX + _fontToSprite[_fontIndex].width - 1;
+ height = _destSpriteY + _fontToSprite[_fontIndex].height - 1;
+ }
+ break;
+
+ default:
+ error("winDraw - Unexpected fct value %d", fct);
+ break;
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if ((i != _vm->_global->_curWinId) && (_fascinWin[i].id != -1)) {
+ if (!_vm->_global->_curWinId || _fascinWin[i].id>_fascinWin[_vm->_global->_curWinId].id) {
+ if ((_fascinWin[i].left + _fascinWin[i].width > left) && (width >= _fascinWin[i].left) &&
+ (_fascinWin[i].top + _fascinWin[i].height > top ) && (height >= _fascinWin[i].top)) {
+ found = true;
+ table[_fascinWin[i].id] = i;
+ }
+ }
+ }
+ }
+
+ if ((_sourceSurface == kBackSurface) && (fct == 0)) {
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
+ _spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
+ _spriteTop + _spriteBottom - 1, _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
+ if (!found)
+ return;
+
+ int j = 0;
+ if (_vm->_global->_curWinId != 0)
+ j = _fascinWin[_vm->_global->_curWinId].id + 1;
+
+ for (int i = 9; i >= j; i--) {
+ if (table[i])
+ _spritesArray[_destSurface]->blit(*_fascinWin[table[i]].savedSurface,
+ _fascinWin[table[i]].left & 7, 0,
+ (_fascinWin[table[i]].left & 7) + _fascinWin[table[i]].width - 1,
+ _fascinWin[table[i]].height - 1, _fascinWin[table[i]].left - _spriteLeft + _destSpriteX,
+ _fascinWin[table[i]].top - _spriteTop + _destSpriteY);
+ }
+ return;
+ }
+
+ if (found) {
+ tempSrf = _vm->_video->initSurfDesc(_vm->_global->_videoMode, width - left + 1, height - top + 1, 0);
+ tempSrf->blit(*_backSurface, left, top, width, height, 0, 0);
+
+ int max = 0;
+ if (_vm->_global->_curWinId != 0)
+ max = _fascinWin[_vm->_global->_curWinId].id + 1;
+
+ for (int i = 9; i >= max; i--) {
+ if (table[i])
+ tempSrf->blit(*_fascinWin[table[i]].savedSurface,
+ _fascinWin[table[i]].left & 7, 0,
+ (_fascinWin[table[i]].left & 7) + _fascinWin[table[i]].width - 1,
+ _fascinWin[table[i]].height - 1,
+ _fascinWin[table[i]].left - left,
+ _fascinWin[table[i]].top - top);
+ }
+
+ invalidateRect(left, top, width, height);
+
+ switch (fct) {
+ case DRAW_BLITSURF: // 0 - move
+ tempSrf->blit(*_spritesArray[_sourceSurface],
+ _spriteLeft, _spriteTop, _spriteLeft + _spriteRight - 1,
+ _spriteTop + _spriteBottom - 1, 0, 0, (_transparency == 0) ? -1 : 0);
+ break;
+
+ case DRAW_PUTPIXEL: // 1 - put a pixel
+ tempSrf->putPixel(0, 0, _frontColor);
+ break;
+
+ case DRAW_FILLRECT: // 2 - fill rectangle
+ tempSrf->fillRect(0, 0, _spriteRight - 1, _spriteBottom - 1, _backColor);
+ break;
+
+ case DRAW_DRAWLINE: // 3 - draw line
+ tempSrf->drawLine(0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
+ break;
+
+ case DRAW_INVALIDATE: // 4 - Draw a circle
+ tempSrf->drawCircle(_spriteRight, _spriteRight, _spriteRight, _frontColor);
+ break;
+
+ case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
+ decompWin(0, 0, tempSrf);
+ break;
+
+ case DRAW_PRINTTEXT: // 6 - Display string
+ len = strlen(_textToPrint);
+ for (int j = 0; j < len; j++)
+ _fonts[_fontIndex]->drawLetter(*tempSrf, _textToPrint[j],
+ j * _fonts[_fontIndex]->getCharWidth(), 0, _frontColor, _backColor, _transparency);
+ _destSpriteX += len * _fonts[_fontIndex]->getCharWidth();
+ break;
+
+ case DRAW_DRAWBAR: // 7 - draw border
+ tempSrf->drawLine(0, _spriteBottom - _destSpriteY, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
+ tempSrf->drawLine(0, 0, 0, _spriteBottom - _destSpriteY, _frontColor);
+ tempSrf->drawLine(_spriteRight - _destSpriteX, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _frontColor);
+ tempSrf->drawLine(0, 0, _spriteRight - _destSpriteX, 0, _frontColor);
+ break;
+
+ case DRAW_CLEARRECT: // 8 - clear rectangle
+ if (_backColor < 16)
+ tempSrf->fillRect(0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _backColor);
+ break;
+
+ case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
+ tempSrf->fillRect(0, 0, _spriteRight - _destSpriteX, _spriteBottom - _destSpriteY, _backColor);
+ break;
+
+ case DRAW_DRAWLETTER: // 10 - Display a character
+ if (_fontToSprite[_fontIndex].sprite == -1) {
+
+ if (_letterToPrint)
+ _fonts[_fontIndex]->drawLetter(*tempSrf, _letterToPrint, 0, 0, _frontColor, _backColor, _transparency);
+ } else {
+ int xx, yy, nn;
+ nn = _spritesArray[_fontToSprite[_fontIndex].sprite]->getWidth() / _fontToSprite[_fontIndex].width;
+ yy = ((_letterToPrint - _fontToSprite[_fontIndex].base) / nn) * _fontToSprite[_fontIndex].height;
+ xx = ((_letterToPrint - _fontToSprite[_fontIndex].base) % nn) * _fontToSprite[_fontIndex].width;
+ tempSrf->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite],
+ xx, yy, xx + _fontToSprite[_fontIndex].width - 1,
+ yy + _fontToSprite[_fontIndex].height - 1, 0, 0, (_transparency == 0) ? -1 : 0);
+ }
+ break;
+
+ default:
+ error("winDraw - Unexpected fct value %d", fct);
+ break;
+ }
+
+ int i = 0;
+ if (_vm->_global->_curWinId != 0)
+ i = _fascinWin[_vm->_global->_curWinId].id + 1;
+
+ for (; i < 10; i++) {
+ if (table[i]) {
+ int k = table[i];
+ _fascinWin[k].savedSurface->blit(*tempSrf,
+ 0, 0, width - left, height - top,
+ left - _fascinWin[k].left + (_fascinWin[k].left & 7),
+ top - _fascinWin[k].top);
+ // Shift skipped as always set to zero (?)
+ tempSrf->blit(*_frontSurface,
+ MAX(left , _fascinWin[k].left),
+ MAX(top , _fascinWin[k].top),
+ MIN(width , (int16) (_fascinWin[k].left + _fascinWin[k].width - 1)),
+ MIN(height, (int16) (_fascinWin[k].top + _fascinWin[k].height - 1)),
+ MAX(left , _fascinWin[k].left) - left,
+ MAX(top , _fascinWin[k].top) - top);
+ if (_cursorIndex != -1)
+ tempSrf->blit(*_cursorSpritesBack,
+ 0, 0, _cursorWidth - 1, _cursorHeight - 1,
+ _cursorX - left, _cursorY - top);
+ for (int j = 9; j > i; j--) {
+ if (table[j] && overlapWin(k, table[j])) {
+ int l = table[j];
+ tempSrf->blit(*_fascinWin[l].savedSurface,
+ MAX(_fascinWin[l].left, _fascinWin[k].left)
+ - _fascinWin[l].left + (_fascinWin[l].left & 7),
+ MAX(_fascinWin[l].top , _fascinWin[k].top ) - _fascinWin[l].top,
+ MIN(_fascinWin[l].left + _fascinWin[l].width - 1, _fascinWin[k].left + _fascinWin[k].width - 1)
+ - _fascinWin[l].left + (_fascinWin[l].left & 7),
+ MIN(_fascinWin[l].top + _fascinWin[l].height - 1, _fascinWin[k].top + _fascinWin[k].height - 1)
+ - _fascinWin[l].top,
+ MAX(_fascinWin[l].left, _fascinWin[k].left) - left,
+ MAX(_fascinWin[l].top , _fascinWin[k].top ) - top);
+ }
+ }
+ }
+ }
+ _backSurface->blit(*tempSrf, 0, 0, width - left, height - top, left, top);
+ tempSrf.reset();
+ } else {
+ invalidateRect(left, top, width, height);
+ switch (fct) {
+ case DRAW_BLITSURF: // 0 - move
+ _backSurface->blit(*_spritesArray[_sourceSurface],
+ _spriteLeft, _spriteTop,
+ _spriteLeft + _spriteRight - 1,
+ _spriteTop + _spriteBottom - 1,
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
+ break;
+ case DRAW_PUTPIXEL: // 1 - put a pixel
+ _backSurface->putPixel(_destSpriteX, _destSpriteY, _frontColor);
+ break;
+
+ case DRAW_FILLRECT: // 2 - fill rectangle
+ _backSurface->fillRect(_destSpriteX, _destSpriteY, _destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1, _backColor);
+ break;
+
+ case DRAW_DRAWLINE: // 3 - draw line
+ _backSurface->drawLine(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _frontColor);
+ break;
+
+ case DRAW_INVALIDATE: // 4 - Draw a circle
+ _backSurface->drawCircle(_spriteRight, _spriteRight, _spriteRight, _frontColor);
+ break;
+
+ case DRAW_LOADSPRITE: // 5 - Uncompress and load a sprite
+ decompWin(_destSpriteX, _destSpriteY, _backSurface);
+ break;
+
+ case DRAW_PRINTTEXT: // 6 - Display string
+ len = strlen(_textToPrint);
+ for (int j = 0; j < len; j++)
+ _fonts[_fontIndex]->drawLetter(*_backSurface, _textToPrint[j],
+ _destSpriteX + j * _fonts[_fontIndex]->getCharWidth(), _destSpriteY,
+ _frontColor, _backColor, _transparency);
+ _destSpriteX += len * _fonts[_fontIndex]->getCharWidth();
+ break;
+
+ case DRAW_DRAWBAR: // 7 - draw border
+ _backSurface->drawLine(_destSpriteX, _spriteBottom, _spriteRight, _spriteBottom, _frontColor);
+ _backSurface->drawLine(_destSpriteX, _destSpriteY, _destSpriteX, _spriteBottom, _frontColor);
+ _backSurface->drawLine(_spriteRight, _destSpriteY, _spriteRight, _spriteBottom, _frontColor);
+ _backSurface->drawLine(_destSpriteX, _destSpriteY, _spriteRight, _destSpriteY, _frontColor);
+ break;
+
+ case DRAW_CLEARRECT: // 8 - clear rectangle
+ if (_backColor < 16)
+ _backSurface->fillRect(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
+ break;
+
+ case DRAW_FILLRECTABS: // 9 - fill rectangle, with other coordinates
+ _backSurface->fillRect(_destSpriteX, _destSpriteY, _spriteRight, _spriteBottom, _backColor);
+ break;
+
+ case DRAW_DRAWLETTER: // 10 - Display a character
+ if (_fontToSprite[_fontIndex].sprite == -1) {
+ if (_letterToPrint)
+ _fonts[_fontIndex]->drawLetter(*_spritesArray[_destSurface], _letterToPrint,
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
+ } else {
+ int xx, yy, nn;
+ nn = _spritesArray[_fontToSprite[_fontIndex].sprite]->getWidth() / _fontToSprite[_fontIndex].width;
+ yy = ((_letterToPrint - _fontToSprite[_fontIndex].base) / nn) * _fontToSprite[_fontIndex].height;
+ xx = ((_letterToPrint - _fontToSprite[_fontIndex].base) % nn) * _fontToSprite[_fontIndex].width;
+ _spritesArray[_destSurface]->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite],
+ xx, yy,
+ xx + _fontToSprite[_fontIndex].width - 1,
+ yy + _fontToSprite[_fontIndex].height - 1,
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
+ }
+ break;
+
+ default:
+ error("winDraw - Unexpected fct value");
+ break;
+ }
+ }
+
+ if (_renderFlags & 16) {
+ if (_sourceSurface == kBackSurface) {
+ _spriteLeft -= _backDeltaX;
+ _spriteTop -= _backDeltaY;
+ }
+ if (_destSurface == kBackSurface) {
+ _destSpriteX -= _backDeltaX;
+ _destSpriteY -= _backDeltaY;
+ }
+ }
+
+ if (_vm->_global->_curWinId) {
+ _destSpriteX -= _fascinWin[_vm->_global->_curWinId].left;
+ _destSpriteY -= _fascinWin[_vm->_global->_curWinId].top;
+ }
+}
+
+void Draw_Fascination::decompWin(int16 x, int16 y, SurfacePtr destPtr) {
+ Resource *resource;
+ resource = _vm->_game->_resources->getResource((uint16) _spriteLeft,
+ &_spriteRight, &_spriteBottom);
+
+ if (!resource)
+ return;
+
+ _vm->_video->drawPackedSprite(resource->getData(),
+ _spriteRight, _spriteBottom, x, y, _transparency, *destPtr);
+
+ delete resource;
+ return;
+}
+
+int16 Draw_Fascination::openWin(int16 id) {
+ if (_fascinWin[id].id != -1)
+ return 0;
+
+ _fascinWin[id].id = _winCount++;
+ _fascinWin[id].left = VAR((_winVarArrayLeft / 4) + id);
+ _fascinWin[id].top = VAR((_winVarArrayTop / 4) + id);
+ _fascinWin[id].width = VAR((_winVarArrayWidth / 4) + id);
+ _fascinWin[id].height = VAR((_winVarArrayHeight / 4) + id);
+
+ _fascinWin[id].savedSurface = _vm->_video->initSurfDesc(_vm->_global->_videoMode, _winMaxWidth + 7, _winMaxHeight, 0);
+
+ saveWin(id);
+ WRITE_VAR((_winVarArrayStatus / 4) + id, VAR((_winVarArrayStatus / 4) + id) & 0xFFFFFFFE);
+
+ return 1;
+}
+
+int16 Draw_Fascination::getWinFromCoord(int16 &dx, int16 &dy) {
+ int16 bestMatch = -1;
+
+ if ((_renderFlags & 128) == 0)
+ return -1;
+
+ for (int i = 0; i < 10; i++) {
+ if (_fascinWin[i].id != -1) {
+ if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left) &&
+ (_vm->_global->_inter_mouseX < _fascinWin[i].left + _fascinWin[i].width) &&
+ (_vm->_global->_inter_mouseY >= _fascinWin[i].top) &&
+ (_vm->_global->_inter_mouseY < _fascinWin[i].top + _fascinWin[i].height)) {
+
+ if (_fascinWin[i].id == _winCount - 1) {
+ dx = _fascinWin[i].left;
+ dy = _fascinWin[i].top;
+ return(i);
+ } else {
+ if (_fascinWin[i].id > bestMatch)
+ bestMatch = _fascinWin[i].id;
+ }
+ }
+ }
+ }
+
+ if (bestMatch != -1)
+ return(0);
+ else
+ return(-1);
+}
+
+void Draw_Fascination::closeWin(int16 id) {
+ if (_fascinWin[id].id == -1)
+ return;
+
+ WRITE_VAR((_winVarArrayStatus / 4) + id, VAR((_winVarArrayStatus / 4) + id) | 1);
+ restoreWin(id);
+ _fascinWin[id].id = -1;
+ _fascinWin[id].savedSurface.reset();
+ _winCount--;
+}
+
+int16 Draw_Fascination::handleCurWin() {
+ int8 matchNum = 0;
+ int16 bestMatch = -1;
+
+ if ((_vm->_game->_mouseButtons != 1) || ((_renderFlags & 128) == 0))
+ return 0;
+
+ for (int i = 0; i < 10; i++) {
+ if (_fascinWin[i].id != -1) {
+ if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left) &&
+ (_vm->_global->_inter_mouseX < _fascinWin[i].left + _fascinWin[i].width) &&
+ (_vm->_global->_inter_mouseY >= _fascinWin[i].top) &&
+ (_vm->_global->_inter_mouseY < _fascinWin[i].top + _fascinWin[i].height)) {
+
+ if (_fascinWin[i].id == _winCount - 1) {
+ if ((_vm->_global->_inter_mouseX < _fascinWin[i].left + 12) &&
+ (_vm->_global->_inter_mouseY < _fascinWin[i].top + 12) &&
+ (VAR(_winVarArrayStatus / 4 + i) & 2)) {
+
+ blitCursor();
+ activeWin(i);
+ closeWin(i);
+ _vm->_util->waitMouseRelease(1);
+ return i;
+ }
+
+ if ((_vm->_global->_inter_mouseX >= _fascinWin[i].left + _fascinWin[i].width - 12) &&
+ (_vm->_global->_inter_mouseY < _fascinWin[i].top + 12) &&
+ (VAR(_winVarArrayStatus / 4 + i) & 4) &&
+ (_vm->_global->_mousePresent) &&
+ (_vm->_global->_videoMode != 0x07)) {
+
+ blitCursor();
+ handleWinBorder(i);
+ moveWin(i);
+ _vm->_global->_inter_mouseX = _fascinWin[i].left + _fascinWin[i].width - 11;
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ return -i;
+ }
+ return 0;
+ } else {
+ if (_fascinWin[i].id > bestMatch) {
+ bestMatch = _fascinWin[i].id;
+ matchNum = i;
+ }
+ }
+ }
+ }
+ }
+
+ if (bestMatch != -1) {
+ blitCursor();
+ activeWin(matchNum);
+ }
+
+ return 0;
+}
+
+void Draw_Fascination::moveWin(int16 id) {
+ int oldLeft = _fascinWin[id].left;
+ int oldTop = _fascinWin[id].top;
+
+ restoreWin(id);
+
+ _fascinWin[id].left = _vm->_global->_inter_mouseX;
+ _fascinWin[id].top = _vm->_global->_inter_mouseY;
+
+ WRITE_VAR((_winVarArrayLeft / 4) + id, _fascinWin[id].left);
+ WRITE_VAR((_winVarArrayTop / 4) + id, _fascinWin[id].top);
+
+ saveWin(id);
+
+ // Shift skipped as always set to zero (?)
+ _backSurface->blit(*_frontSurface,
+ oldLeft, oldTop,
+ oldLeft + _fascinWin[id].width - 1,
+ oldTop + _fascinWin[id].height - 1,
+ _fascinWin[id].left, _fascinWin[id].top);
+ invalidateRect(_fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1);
+}
+
+bool Draw_Fascination::overlapWin(int16 idWin1, int16 idWin2) {
+ if ((_fascinWin[idWin1].left + _fascinWin[idWin1].width <= _fascinWin[idWin2].left) ||
+ (_fascinWin[idWin2].left + _fascinWin[idWin2].width <= _fascinWin[idWin1].left) ||
+ (_fascinWin[idWin1].top + _fascinWin[idWin1].height <= _fascinWin[idWin2].top ) ||
+ (_fascinWin[idWin2].top + _fascinWin[idWin2].height <= _fascinWin[idWin1].top ))
+ return false;
+
+ return true;
+}
+
+void Draw_Fascination::activeWin(int16 id) {
+ bool found = false;
+ int16 t[10], t2[10];
+ int nextId = -1;
+ int oldId = -1;
+ SurfacePtr tempSrf;
+ SurfacePtr oldSrf[10];
+
+ if (_fascinWin[id].id == -1)
+ return;
+
+ blitInvalidated();
+
+ for (int i = 0; i < 10; i++) {
+ t[i] = -1;
+ t2[i] = -1;
+ oldSrf[i].reset();
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if ((i != id) && (_fascinWin[i].id > _fascinWin[id].id) && (overlapWin(i, id))) {
+ t[_fascinWin[i].id] = i;
+ found = true;
+ }
+ }
+
+ if (found) {
+ for (int i = 9; i >= 0; i--) {
+ if (t[i] != -1) {
+ if (nextId != -1)
+ _fascinWin[nextId].savedSurface->blit(*_backSurface,
+ _fascinWin[t[i]].left, _fascinWin[t[i]].top,
+ _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
+ _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
+ _fascinWin[t[i]].left & 7, 0);
+ t2[i] = nextId;
+ restoreWin(t[i]);
+ nextId = t[i];
+ }
+ }
+
+ oldId = nextId;
+ _fascinWin[nextId].savedSurface->blit(*_backSurface,
+ _fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1,
+ _fascinWin[id].left & 7, 0);
+ restoreWin(id);
+ nextId = id;
+
+ for (int i = 0; i < 10; i++) {
+ if (t[i] != -1) {
+ _fascinWin[nextId].savedSurface->blit(*_backSurface,
+ _fascinWin[t[i]].left, _fascinWin[t[i]].top,
+ _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
+ _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
+ _fascinWin[t[i]].left & 7, 0);
+ oldSrf[t[i]] = _fascinWin[nextId].savedSurface;
+ if (t2[i] != -1)
+ _backSurface->blit(*_fascinWin[t2[i]].savedSurface,
+ _fascinWin[t[i]].left & 7, 0,
+ (_fascinWin[t[i]].left & 7) + _fascinWin[t[i]].width - 1,
+ _fascinWin[t[i]].height - 1, _fascinWin[t[i]].left,
+ _fascinWin[t[i]].top);
+ else {
+ // Shift skipped as always set to zero (?)
+ _backSurface->blit(*_frontSurface,
+ _fascinWin[t[i]].left, _fascinWin[t[i]].top,
+ _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
+ _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1,
+ _fascinWin[t[i]].left, _fascinWin[t[i]].top);
+ }
+ invalidateRect(_fascinWin[t[i]].left, _fascinWin[t[i]].top,
+ _fascinWin[t[i]].left + _fascinWin[t[i]].width - 1,
+ _fascinWin[t[i]].top + _fascinWin[t[i]].height - 1);
+ nextId = t2[i];
+ }
+ }
+
+ tempSrf = _vm->_video->initSurfDesc(_vm->_global->_videoMode, _winMaxWidth + 7, _winMaxHeight, 0);
+ tempSrf->blit(*_backSurface,
+ _fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1,
+ _fascinWin[id].left & 7, 0);
+ _backSurface->blit(*_fascinWin[oldId].savedSurface,
+ _fascinWin[id].left & 7, 0,
+ (_fascinWin[id].left & 7) + _fascinWin[id].width - 1,
+ _fascinWin[id].height - 1,
+ _fascinWin[id].left, _fascinWin[id].top);
+
+ _fascinWin[oldId].savedSurface.reset();
+ _fascinWin[oldId].savedSurface = tempSrf;
+ oldSrf[id] = _fascinWin[oldId].savedSurface;
+
+ invalidateRect(_fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1);
+ nextId = id;
+
+ for (int j = 0; j < 10; j++) {
+ if (oldSrf[j] != 0)
+ _fascinWin[j].savedSurface = oldSrf[j];
+ }
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if ((i != id) && (_fascinWin[i].id > _fascinWin[id].id))
+ _fascinWin[i].id--;
+ }
+
+ _fascinWin[id].id = _winCount - 1;
+}
+
+void Draw_Fascination::closeAllWin() {
+ for (int i = 0; i < 10; i++) {
+ activeWin(i);
+ closeWin(i);
+ }
+}
+
+void Draw_Fascination::saveWin(int16 id) {
+ _fascinWin[id].savedSurface->blit(*_backSurface,
+ _fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1,
+ _fascinWin[id].left & 7, 0);
+}
+
+void Draw_Fascination::restoreWin(int16 id) {
+ _backSurface->blit(*_fascinWin[id].savedSurface,
+ _fascinWin[id].left & 7, 0,
+ (_fascinWin[id].left & 7) + _fascinWin[id].width - 1, _fascinWin[id].height - 1,
+ _fascinWin[id].left, _fascinWin[id].top);
+ invalidateRect(_fascinWin[id].left, _fascinWin[id].top,
+ _fascinWin[id].left + _fascinWin[id].width - 1,
+ _fascinWin[id].top + _fascinWin[id].height - 1);
+}
+
+void Draw_Fascination::drawWinTrace(int16 left, int16 top, int16 width, int16 height) {
+ int16 right, bottom;
+
+ right = left + width - 1;
+ bottom = top + height - 1;
+
+ Pixel pixelTop = _frontSurface->get(left, top);
+ Pixel pixelBottom = _frontSurface->get(left, bottom);
+ for (int16 i = 0; i < width; i++, pixelTop++, pixelBottom++) {
+ pixelTop.set((pixelTop.get() + 128) & 0xFF);
+ pixelBottom.set((pixelBottom.get() + 128) & 0xFF);
+ }
+
+ Pixel pixelLeft = _frontSurface->get(left, top);
+ Pixel pixelRight = _frontSurface->get(right, top);
+
+ for (int16 i = 0; i < height; i++, pixelLeft += _frontSurface->getWidth(), pixelRight += _frontSurface->getWidth()) {
+ pixelLeft.set((pixelLeft.get() + 128) & 0xFF);
+ pixelRight.set((pixelRight.get() + 128) & 0xFF);
+ }
+
+ _vm->_video->dirtyRectsAll();
+ _vm->_video->retrace(true);
+}
+
+void Draw_Fascination::handleWinBorder(int16 id) {
+ int16 minX = 0;
+ int16 maxX = 320;
+ int16 minY = 0;
+ int16 maxY = 200;
+
+ if (VAR((_winVarArrayStatus / 4) + id) & 8)
+ minX = (int16)(VAR((_winVarArrayLimitsX / 4) + id) >> 16L);
+ if (VAR((_winVarArrayStatus / 4) + id) & 16)
+ maxX = (int16)(VAR((_winVarArrayLimitsX / 4) + id) & 0xFFFFL);
+ if (VAR((_winVarArrayStatus / 4) + id) & 32)
+ minY = (int16)(VAR((_winVarArrayLimitsY / 4) + id) >> 16L);
+ if (VAR((_winVarArrayStatus / 4) + id) & 64)
+ maxY = (int16)(VAR((_winVarArrayLimitsY / 4) + id) & 0xFFFFL);
+
+ _vm->_global->_inter_mouseX = _fascinWin[id].left;
+ _vm->_global->_inter_mouseY = _fascinWin[id].top;
+
+ if (_vm->_global->_mousePresent)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+
+ drawWinTrace(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY, _fascinWin[id].width, _fascinWin[id].height);
+ _cursorX = _vm->_global->_inter_mouseX;
+ _cursorY = _vm->_global->_inter_mouseY;
+
+ do {
+ _vm->_game->checkKeys(&_vm->_global->_inter_mouseX, &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, 1);
+
+ if (_vm->_global->_inter_mouseX != _cursorX || _vm->_global->_inter_mouseY != _cursorY) {
+ if (_vm->_global->_inter_mouseX < minX) {
+ _vm->_global->_inter_mouseX = minX;
+ if (_vm->_global->_mousePresent)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ }
+
+ if (_vm->_global->_inter_mouseY < minY) {
+ _vm->_global->_inter_mouseY = minY;
+ if (_vm->_global->_mousePresent)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ }
+
+ if (_vm->_global->_inter_mouseX + _fascinWin[id].width > maxX) {
+ _vm->_global->_inter_mouseX = maxX - _fascinWin[id].width;
+ if (_vm->_global->_mousePresent)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ }
+
+ if (_vm->_global->_inter_mouseY + _fascinWin[id].height > maxY) {
+ _vm->_global->_inter_mouseY = maxY - _fascinWin[id].height;
+ if (_vm->_global->_mousePresent)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ }
+
+ drawWinTrace(_cursorX, _cursorY, _fascinWin[id].width, _fascinWin[id].height);
+ drawWinTrace(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY, _fascinWin[id].width, _fascinWin[id].height);
+ _cursorX = _vm->_global->_inter_mouseX;
+ _cursorY = _vm->_global->_inter_mouseY;
+ }
+ } while (_vm->_game->_mouseButtons);
+ drawWinTrace(_cursorX, _cursorY, _fascinWin[id].width, _fascinWin[id].height);
+ _cursorX = _vm->_global->_inter_mouseX;
+ _cursorY = _vm->_global->_inter_mouseY;
+}
+
} // End of namespace Gob
diff --git a/engines/gob/draw_playtoons.cpp b/engines/gob/draw_playtoons.cpp
index 583d13986e..8d8f040924 100644
--- a/engines/gob/draw_playtoons.cpp
+++ b/engines/gob/draw_playtoons.cpp
@@ -38,7 +38,7 @@ Draw_Playtoons::Draw_Playtoons(GobEngine *vm) : Draw_v2(vm) {
void Draw_Playtoons::spriteOperation(int16 operation) {
int16 len;
int16 x, y;
- SurfaceDescPtr sourceSurf, destSurf;
+ SurfacePtr sourceSurf, destSurf;
bool deltaVeto;
int16 left;
int16 ratio;
@@ -139,12 +139,11 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
if (!sourceSurf || !destSurf)
break;
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface],
- *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
_spriteLeft, spriteTop,
_spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
@@ -156,22 +155,22 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
warning("oPlaytoons_spriteOperation: operation DRAW_PUTPIXEL, pattern -1");
break;
case 1:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX,
+ _spritesArray[_destSurface]->fillRect(destSpriteX,
_destSpriteY, _destSpriteX + 1,
_destSpriteY + 1, _frontColor);
break;
case 2:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX - 1,
+ _spritesArray[_destSurface]->fillRect(destSpriteX - 1,
_destSpriteY - 1, _destSpriteX + 1,
_destSpriteY + 1, _frontColor);
break;
case 3:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX - 1,
+ _spritesArray[_destSurface]->fillRect(destSpriteX - 1,
_destSpriteY - 1, _destSpriteX + 2,
_destSpriteY + 2, _frontColor);
break;
default:
- _vm->_video->putPixel(_destSpriteX, _destSpriteY, _frontColor, *_spritesArray[_destSurface]);
+ _spritesArray[_destSurface]->putPixel(_destSpriteX, _destSpriteY, _frontColor);
break;
}
dirtiedRect(_destSurface, _destSpriteX - (_pattern / 2),
@@ -188,7 +187,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
warning("oPlaytoons_spriteOperation: operation DRAW_FILLRECT, pattern %d", _pattern & 0xFF);
break;
case 0:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX,
+ _spritesArray[_destSurface]->fillRect(destSpriteX,
_destSpriteY, _destSpriteX + _spriteRight - 1,
_destSpriteY + _spriteBottom - 1, _backColor);
@@ -204,23 +203,18 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
case DRAW_DRAWLINE:
if ((_needAdjust != 2) && (_needAdjust < 10)) {
warning ("oPlaytoons_spriteOperation: operation DRAW_DRAWLINE, draw multiple lines");
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX + 1, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX + 1, _destSpriteY,
_spriteRight + 1, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY + 1,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY + 1,
_spriteRight, _spriteBottom + 1, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX + 1, _destSpriteY + 1,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX + 1, _destSpriteY + 1,
_spriteRight + 1, _spriteBottom + 1, _frontColor);
} else {
switch (_pattern & 0xFF) {
case 0:
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
break;
@@ -228,7 +222,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
warning("oPlaytoons_spriteOperation: operation DRAW_DRAWLINE, draw %d lines", (_pattern & 0xFF) * (_pattern & 0xFF));
for (int16 i = 0; i <= _pattern ; i++)
for (int16 j = 0; j <= _pattern ; j++)
- _vm->_video->drawLine(*_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->drawLine(
_destSpriteX - (_pattern / 2) + i,
_destSpriteY - (_pattern / 2) + j,
_spriteRight - (_pattern / 2) + i,
@@ -247,7 +241,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
if ((_pattern & 0xFF) != 0)
warning("oPlaytoons_spriteOperation: operation DRAW_INVALIDATE, pattern %d", _pattern & 0xFF);
- _vm->_video->drawCircle(*_spritesArray[_destSurface], _destSpriteX,
+ _spritesArray[_destSurface]->drawCircle(_destSpriteX,
_destSpriteY, _spriteRight, _frontColor);
dirtiedRect(_destSurface, _destSpriteX - _spriteRight, _destSpriteY - _spriteBottom,
@@ -289,9 +283,8 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
byte *dataBuf = _vm->_game->_resources->getTexts() + _textToPrint[1] + 1;
len = *dataBuf++;
for (int i = 0; i < len; i++, dataBuf += 2) {
- _vm->_video->drawLetter(READ_LE_UINT16(dataBuf), _destSpriteX,
- _destSpriteY, *font, _transparency, _frontColor,
- _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], READ_LE_UINT16(dataBuf),
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
}
} else {
drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor,
@@ -300,9 +293,8 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
}
} else {
for (int i = 0; i < len; i++) {
- _vm->_video->drawLetter(_textToPrint[i], _destSpriteX,
- _destSpriteY, *font, _transparency,
- _frontColor, _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], _textToPrint[i],
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
_destSpriteX += font->getCharWidth(_textToPrint[i]);
}
}
@@ -317,11 +309,10 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
* _fontToSprite[_fontIndex].height;
x = ((_textToPrint[i] - _fontToSprite[_fontIndex].base) % ratio)
* _fontToSprite[_fontIndex].width;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite],
- *_spritesArray[_destSurface], x, y,
+ _spritesArray[_destSurface]->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite], x, y,
x + _fontToSprite[_fontIndex].width - 1,
y + _fontToSprite[_fontIndex].height - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
_destSpriteX += _fontToSprite[_fontIndex].width;
}
}
@@ -332,36 +323,28 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
case DRAW_DRAWBAR:
if ((_needAdjust != 2) && (_needAdjust < 10)){
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom - 1,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _spriteBottom - 1,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_destSpriteX + 1, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _spriteRight - 1, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_spriteRight - 1, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY + 1, _frontColor);
} else {
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _spriteBottom,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_destSpriteX, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _spriteRight, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_spriteRight, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY, _frontColor);
}
@@ -371,8 +354,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
case DRAW_CLEARRECT:
warning ("oPlaytoons_spriteOperation: DRAW_CLEARRECT uses _backColor %d", _backColor);
if (_backColor != -1) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom,
_backColor);
}
@@ -381,8 +363,7 @@ void Draw_Playtoons::spriteOperation(int16 operation) {
break;
case DRAW_FILLRECTABS:
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _backColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
diff --git a/engines/gob/draw_v1.cpp b/engines/gob/draw_v1.cpp
index 1cec15ce04..6496229282 100644
--- a/engines/gob/draw_v1.cpp
+++ b/engines/gob/draw_v1.cpp
@@ -119,13 +119,13 @@ void Draw_v1::animateCursor(int16 cursor) {
newY -= hotspotY = (uint16) VAR(_cursorIndex + _cursorHotspotYVar);
}
- _vm->_video->clearSurf(*_scummvmCursor);
- _vm->_video->drawSprite(*_cursorSprites, *_scummvmCursor,
+ _scummvmCursor->clear();
+ _scummvmCursor->blit(*_cursorSprites,
cursorIndex * _cursorWidth, 0,
(cursorIndex + 1) * _cursorWidth - 1,
- _cursorHeight - 1, 0, 0, 0);
- CursorMan.replaceCursor(_scummvmCursor->getVidMem(),
- _cursorWidth, _cursorHeight, hotspotX, hotspotY, 0);
+ _cursorHeight - 1, 0, 0);
+ CursorMan.replaceCursor(_scummvmCursor->getData(),
+ _cursorWidth, _cursorHeight, hotspotX, hotspotY, 0, 1, &_vm->getPixelFormat());
if (_frontSurface != _backSurface) {
_showCursor = 3;
@@ -346,27 +346,24 @@ void Draw_v1::spriteOperation(int16 operation) {
Font *font = 0;
switch (operation) {
case DRAW_BLITSURF:
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface],
- *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
_spriteLeft, _spriteTop,
_spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
break;
case DRAW_PUTPIXEL:
- _vm->_video->putPixel(_destSpriteX, _destSpriteY,
- _frontColor, *_spritesArray[_destSurface]);
+ _spritesArray[_destSurface]->putPixel(_destSpriteX, _destSpriteY, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _destSpriteX, _destSpriteY);
break;
case DRAW_FILLRECT:
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_destSpriteX + _spriteRight - 1,
_destSpriteY + _spriteBottom - 1, _backColor);
@@ -375,8 +372,7 @@ void Draw_v1::spriteOperation(int16 operation) {
break;
case DRAW_DRAWLINE:
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
@@ -417,31 +413,24 @@ void Draw_v1::spriteOperation(int16 operation) {
_destSpriteY + font->getCharHeight() - 1);
for (int i = 0; i < len; i++) {
- _vm->_video->drawLetter(_textToPrint[i],
- _destSpriteX, _destSpriteY,
- *font, _transparency,
- _frontColor, _backColor,
- *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], _textToPrint[i],
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
_destSpriteX += font->getCharWidth();
}
break;
case DRAW_DRAWBAR:
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _spriteBottom,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_destSpriteX, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _spriteRight, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_spriteRight, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
@@ -449,8 +438,7 @@ void Draw_v1::spriteOperation(int16 operation) {
case DRAW_CLEARRECT:
if (_backColor < 16) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom,
_backColor);
}
@@ -458,8 +446,7 @@ void Draw_v1::spriteOperation(int16 operation) {
break;
case DRAW_FILLRECTABS:
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _backColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
@@ -476,11 +463,8 @@ void Draw_v1::spriteOperation(int16 operation) {
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
_destSpriteX + font->getCharWidth() - 1,
_destSpriteY + font->getCharHeight() - 1);
- _vm->_video->drawLetter(_letterToPrint,
- _destSpriteX, _destSpriteY,
- *font, _transparency,
- _frontColor, _backColor,
- *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], _letterToPrint,
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
break;
}
@@ -498,11 +482,10 @@ void Draw_v1::spriteOperation(int16 operation) {
_destSpriteX + _fontToSprite[_fontIndex].width,
_destSpriteY + _fontToSprite[_fontIndex].height);
- _vm->_video->drawSprite(*_spritesArray[(int16)_fontToSprite[_fontIndex].sprite],
- *_spritesArray[_destSurface], x, y,
+ _spritesArray[_destSurface]->blit(*_spritesArray[(int16)_fontToSprite[_fontIndex].sprite], x, y,
x + _fontToSprite[_fontIndex].width,
y + _fontToSprite[_fontIndex].height,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
break;
}
diff --git a/engines/gob/draw_v2.cpp b/engines/gob/draw_v2.cpp
index 5d001f4b59..bb2a57b790 100644
--- a/engines/gob/draw_v2.cpp
+++ b/engines/gob/draw_v2.cpp
@@ -53,7 +53,7 @@ void Draw_v2::initScreen() {
initSpriteSurf(kBackSurface, _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0);
_backSurface = _spritesArray[kBackSurface];
- _vm->_video->clearSurf(*_backSurface);
+ _backSurface->clear();
if (!_spritesArray[kCursorSurface]) {
initSpriteSurf(kCursorSurface, 32, 16, 2);
@@ -144,13 +144,13 @@ void Draw_v2::animateCursor(int16 cursor) {
newY -= hotspotY = (uint16) VAR(_cursorIndex + _cursorHotspotYVar);
}
- _vm->_video->clearSurf(*_scummvmCursor);
- _vm->_video->drawSprite(*_cursorSprites, *_scummvmCursor,
+ _scummvmCursor->clear();
+ _scummvmCursor->blit(*_cursorSprites,
cursorIndex * _cursorWidth, 0,
(cursorIndex + 1) * _cursorWidth - 1,
- _cursorHeight - 1, 0, 0, 0);
- CursorMan.replaceCursor(_scummvmCursor->getVidMem(),
- _cursorWidth, _cursorHeight, hotspotX, hotspotY, 0);
+ _cursorHeight - 1, 0, 0);
+ CursorMan.replaceCursor(_scummvmCursor->getData(),
+ _cursorWidth, _cursorHeight, hotspotX, hotspotY, 0, 1, &_vm->getPixelFormat());
if (_frontSurface != _backSurface) {
if (!_noInvalidated) {
@@ -230,7 +230,10 @@ void Draw_v2::printTotText(int16 id) {
destX = (READ_LE_UINT16(ptr) & 0x7FFF) * 2;
spriteRight = READ_LE_UINT16(ptr + 4) * 2 + 1;
} else {
- destX = READ_LE_UINT16(ptr) & 0x7FFF;
+// No mask used for Fascination
+ destX = READ_LE_UINT16(ptr);
+ if (_vm->getGameType() != kGameTypeFascination)
+ destX &= 0x7FFF;
spriteRight = READ_LE_UINT16(ptr + 4);
}
@@ -614,7 +617,7 @@ void Draw_v2::printTotText(int16 id) {
void Draw_v2::spriteOperation(int16 operation) {
int16 len;
int16 x, y;
- SurfaceDescPtr sourceSurf, destSurf;
+ SurfacePtr sourceSurf, destSurf;
bool deltaVeto;
int16 left;
int16 ratio;
@@ -715,26 +718,24 @@ void Draw_v2::spriteOperation(int16 operation) {
if (!sourceSurf || !destSurf)
break;
- _vm->_video->drawSprite(*_spritesArray[_sourceSurface],
- *_spritesArray[_destSurface],
+ _spritesArray[_destSurface]->blit(*_spritesArray[_sourceSurface],
_spriteLeft, spriteTop,
_spriteLeft + _spriteRight - 1,
_spriteTop + _spriteBottom - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY,
_destSpriteX + _spriteRight - 1, _destSpriteY + _spriteBottom - 1);
break;
case DRAW_PUTPIXEL:
- _vm->_video->putPixel(_destSpriteX, _destSpriteY, _frontColor,
- *_spritesArray[_destSurface]);
+ _spritesArray[_destSurface]->putPixel(_destSpriteX, _destSpriteY, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _destSpriteX, _destSpriteY);
break;
case DRAW_FILLRECT:
- _vm->_video->fillRect(*_spritesArray[_destSurface], destSpriteX,
+ _spritesArray[_destSurface]->fillRect(destSpriteX,
_destSpriteY, _destSpriteX + _spriteRight - 1,
_destSpriteY + _spriteBottom - 1, _backColor);
@@ -743,15 +744,14 @@ void Draw_v2::spriteOperation(int16 operation) {
break;
case DRAW_DRAWLINE:
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
break;
case DRAW_INVALIDATE:
- _vm->_video->drawCircle(*_spritesArray[_destSurface], _destSpriteX,
+ _spritesArray[_destSurface]->drawCircle(_destSpriteX,
_destSpriteY, _spriteRight, _frontColor);
dirtiedRect(_destSurface, _destSpriteX - _spriteRight, _destSpriteY - _spriteBottom,
@@ -793,9 +793,8 @@ void Draw_v2::spriteOperation(int16 operation) {
byte *dataBuf = _vm->_game->_resources->getTexts() + _textToPrint[1] + 1;
len = *dataBuf++;
for (int i = 0; i < len; i++, dataBuf += 2) {
- _vm->_video->drawLetter(READ_LE_UINT16(dataBuf), _destSpriteX,
- _destSpriteY, *font, _transparency, _frontColor,
- _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], READ_LE_UINT16(dataBuf),
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
}
} else {
drawString(_textToPrint, _destSpriteX, _destSpriteY, _frontColor,
@@ -804,9 +803,8 @@ void Draw_v2::spriteOperation(int16 operation) {
}
} else {
for (int i = 0; i < len; i++) {
- _vm->_video->drawLetter(_textToPrint[i], _destSpriteX,
- _destSpriteY, *font, _transparency,
- _frontColor, _backColor, *_spritesArray[_destSurface]);
+ font->drawLetter(*_spritesArray[_destSurface], _textToPrint[i],
+ _destSpriteX, _destSpriteY, _frontColor, _backColor, _transparency);
_destSpriteX += font->getCharWidth(_textToPrint[i]);
}
}
@@ -821,11 +819,10 @@ void Draw_v2::spriteOperation(int16 operation) {
* _fontToSprite[_fontIndex].height;
x = ((_textToPrint[i] - _fontToSprite[_fontIndex].base) % ratio)
* _fontToSprite[_fontIndex].width;
- _vm->_video->drawSprite(*_spritesArray[_fontToSprite[_fontIndex].sprite],
- *_spritesArray[_destSurface], x, y,
+ _spritesArray[_destSurface]->blit(*_spritesArray[_fontToSprite[_fontIndex].sprite], x, y,
x + _fontToSprite[_fontIndex].width - 1,
y + _fontToSprite[_fontIndex].height - 1,
- _destSpriteX, _destSpriteY, _transparency);
+ _destSpriteX, _destSpriteY, (_transparency == 0) ? -1 : 0);
_destSpriteX += _fontToSprite[_fontIndex].width;
}
}
@@ -836,36 +833,28 @@ void Draw_v2::spriteOperation(int16 operation) {
case DRAW_DRAWBAR:
if (_needAdjust != 2) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom - 1,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _spriteBottom - 1,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_destSpriteX + 1, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _spriteRight - 1, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_spriteRight - 1, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY + 1, _frontColor);
} else {
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _spriteBottom,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _spriteBottom,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_destSpriteX, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _spriteRight, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_spriteRight, _destSpriteY,
_spriteRight, _spriteBottom, _frontColor);
- _vm->_video->drawLine(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->drawLine(_destSpriteX, _destSpriteY,
_spriteRight, _destSpriteY, _frontColor);
}
@@ -874,8 +863,7 @@ void Draw_v2::spriteOperation(int16 operation) {
case DRAW_CLEARRECT:
if ((_backColor != 16) && (_backColor != 144)) {
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom,
_backColor);
}
@@ -884,8 +872,7 @@ void Draw_v2::spriteOperation(int16 operation) {
break;
case DRAW_FILLRECTABS:
- _vm->_video->fillRect(*_spritesArray[_destSurface],
- _destSpriteX, _destSpriteY,
+ _spritesArray[_destSurface]->fillRect(_destSpriteX, _destSpriteY,
_spriteRight, _spriteBottom, _backColor);
dirtiedRect(_destSurface, _destSpriteX, _destSpriteY, _spriteRight, _spriteBottom);
diff --git a/engines/gob/driver_vga.cpp b/engines/gob/driver_vga.cpp
deleted file mode 100644
index c93962c206..0000000000
--- a/engines/gob/driver_vga.cpp
+++ /dev/null
@@ -1,244 +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/endian.h"
-#include "graphics/primitives.h"
-
-#include "gob/driver_vga.h"
-
-namespace Gob {
-
-static void plotPixel(int x, int y, int color, void *data) {
- SurfaceDesc *dest = (SurfaceDesc *)data;
-
- if ((x >= 0) && (x < dest->getWidth()) &&
- (y >= 0) && (y < dest->getHeight()))
- dest->getVidMem()[(y * dest->getWidth()) + x] = color;
-}
-
-void VGAVideoDriver::putPixel(int16 x, int16 y, byte color, SurfaceDesc &dest) {
- if ((x >= 0) && (x < dest.getWidth()) &&
- (y >= 0) && (y < dest.getHeight()))
- dest.getVidMem()[(y * dest.getWidth()) + x] = color;
-}
-
-void VGAVideoDriver::drawLine(SurfaceDesc &dest, int16 x0, int16 y0, int16 x1,
- int16 y1, byte color) {
-
- Graphics::drawLine(x0, y0, x1, y1, color, &plotPixel, &dest);
-}
-
-void VGAVideoDriver::fillRect(SurfaceDesc &dest, int16 left, int16 top,
- int16 right, int16 bottom, byte color) {
-
- if ((left >= dest.getWidth()) || (right >= dest.getWidth()) ||
- (top >= dest.getHeight()) || (bottom >= dest.getHeight()))
- return;
-
- byte *pos = dest.getVidMem() + (top * dest.getWidth()) + left;
- int16 width = (right - left) + 1;
- int16 height = (bottom - top) + 1;
-
- while (height--) {
- for (int16 i = 0; i < width; ++i)
- pos[i] = color;
-
- pos += dest.getWidth();
- }
-}
-
-void VGAVideoDriver::drawLetter(unsigned char item, int16 x, int16 y,
- const Font &font, byte color1, byte color2,
- byte transp, SurfaceDesc &dest) {
- uint16 data;
-
- const byte *src = font.getCharData(item);
- byte *dst = dest.getVidMem() + x + dest.getWidth() * y;
-
- int nWidth = font.getCharWidth();
-
- if (nWidth & 7)
- nWidth = (nWidth & 0xF8) + 8;
-
- nWidth >>= 3;
-
- for (int i = 0; i < font.getCharHeight(); i++) {
- int width = font.getCharWidth();
-
- for (int k = 0; k < nWidth; k++) {
-
- data = *src++;
- for (int j = 0; j < MIN(8, width); j++) {
- if (data & 0x80)
- *dst = color2;
- else if (color1 == 0)
- *dst = transp;
-
- dst++;
- data <<= 1;
- }
-
- width -= 8;
-
- }
-
- dst += dest.getWidth() - font.getCharWidth();
- }
-}
-
-void VGAVideoDriver::drawSprite(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom,
- int16 x, int16 y, int16 transp) {
-
- if ((x >= dest.getWidth()) || (x < 0) ||
- (y >= dest.getHeight()) || (y < 0))
- return;
-
- int16 width = MIN((right - left) + 1, (int) dest.getWidth());
- int16 height = MIN((bottom - top) + 1, (int) dest.getHeight());
-
- if ((width < 1) || (height < 1))
- return;
-
- const byte *srcPos = source.getVidMem() + (top * source.getWidth()) + left;
- byte *destPos = dest.getVidMem() + (y * dest.getWidth()) + x;
-
- uint32 size = width * height;
-
- if (transp) {
-
- while (height--) {
- for (int16 i = 0; i < width; ++i) {
- if (srcPos[i])
- destPos[i] = srcPos[i];
- }
-
- srcPos += source.getWidth();
- destPos += dest.getWidth();
- }
-
- } else if (((srcPos >= destPos) && (srcPos <= (destPos + size))) ||
- ((destPos >= srcPos) && (destPos <= (srcPos + size)))) {
-
- while (height--) {
- memmove(destPos, srcPos, width);
-
- srcPos += source.getWidth();
- destPos += dest.getWidth();
- }
-
- } else {
-
- while (height--) {
- memcpy(destPos, srcPos, width);
-
- srcPos += source.getWidth();
- destPos += dest.getWidth();
- }
-
- }
-}
-
-void VGAVideoDriver::drawSpriteDouble(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom,
- int16 x, int16 y, int16 transp) {
-
- if ((x >= dest.getWidth()) || (x < 0) ||
- (y >= dest.getHeight()) || (y < 0))
- return;
-
- int16 width = MIN<int>((right - left) + 1, dest.getWidth() / 2);
- int16 height = MIN<int>((bottom - top) + 1, dest.getHeight() / 2);
-
- if ((width < 1) || (height < 1))
- return;
-
- const byte *srcPos = source.getVidMem() + (top * source.getWidth()) + left;
- byte *destPos = dest.getVidMem() + ((y * 2) * dest.getWidth()) + (x * 2);
-
- while (height--) {
- const byte *srcBak = srcPos;
-
- for (int i = 0; i < 2; i++) {
- srcPos = srcBak;
-
- for (int16 j = 0; j < width; j++) {
- if (!transp || srcPos[i]) {
- destPos[2 * j + 0] = srcPos[j];
- destPos[2 * j + 1] = srcPos[j];
- }
- }
-
- destPos += dest.getWidth();
- }
-
- srcPos = srcBak + source.getWidth();
- }
-}
-
-void VGAVideoDriver::drawPackedSprite(byte *sprBuf, int16 width, int16 height,
- int16 x, int16 y, byte transp, SurfaceDesc &dest) {
- int destRight = x + width;
- int destBottom = y + height;
-
- byte *dst = dest.getVidMem() + x + dest.getWidth() * y;
-
- int curx = x;
- int cury = y;
-
- while (1) {
- uint8 val = *sprBuf++;
- unsigned int repeat = val & 7;
- val &= 0xF8;
-
- if (!(val & 8)) {
- repeat <<= 8;
- repeat |= *sprBuf++;
- }
- repeat++;
- val >>= 4;
-
- for (unsigned int i = 0; i < repeat; ++i) {
- if (curx < dest.getWidth() && cury < dest.getHeight())
- if (!transp || val)
- *dst = val;
-
- dst++;
- curx++;
- if (curx == destRight) {
- dst += dest.getWidth() + x - curx;
- curx = x;
- cury++;
- if (cury == destBottom)
- return;
- }
- }
-
- }
-
-}
-
-}
-
diff --git a/engines/gob/driver_vga.h b/engines/gob/driver_vga.h
deleted file mode 100644
index 3102016cb5..0000000000
--- a/engines/gob/driver_vga.h
+++ /dev/null
@@ -1,56 +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 GOB_DRIVER_VGA_H
-#define GOB_DRIVER_VGA_H
-
-#include "gob/video.h"
-
-namespace Gob {
-
-class VGAVideoDriver : public VideoDriver {
-public:
- VGAVideoDriver() {}
- virtual ~VGAVideoDriver() {}
-
- void putPixel(int16 x, int16 y, byte color, SurfaceDesc &dest);
- void drawLine(SurfaceDesc &dest, int16 x0, int16 y0,
- int16 x1, int16 y1, byte color);
- void fillRect(SurfaceDesc &dest, int16 left, int16 top,
- int16 right, int16 bottom, byte color);
- void drawLetter(unsigned char item, int16 x, int16 y,
- const Font &font, byte color1, byte color2,
- byte transp, SurfaceDesc &dest);
- void drawSprite(SurfaceDesc &source, SurfaceDesc &dest, int16 left,
- int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp);
- void drawSpriteDouble(SurfaceDesc &source, SurfaceDesc &dest, int16 left,
- int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp);
- void drawPackedSprite(byte *sprBuf, int16 width, int16 height,
- int16 x, int16 y, byte transp, SurfaceDesc &dest);
-};
-
-}
-
-#endif // GOB_DRIVER_VGA_H
diff --git a/engines/gob/expression.cpp b/engines/gob/expression.cpp
index 9652862b2f..d053345b4c 100644
--- a/engines/gob/expression.cpp
+++ b/engines/gob/expression.cpp
@@ -1002,7 +1002,6 @@ int16 Expression::parseExpr(byte stopToken, byte *type) {
Stack stack;
StackFrame stackFrame(stack);
byte operation;
- bool escape;
int16 brackStart;
uint32 varBase;
@@ -1037,7 +1036,6 @@ int16 Expression::parseExpr(byte stopToken, byte *type) {
if ((operation == stopToken) || (operation == OP_OR) ||
(operation == OP_AND) || (operation == OP_END_EXPR)) {
while (stackFrame.pos >= 2) {
- escape = false;
if ((stackFrame.opers[-2] == OP_BEGIN_EXPR) &&
((operation == OP_END_EXPR) || (operation == stopToken))) {
stackFrame.opers[-2] = stackFrame.opers[-1];
diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp
index 1a8823b156..f77b3e946a 100644
--- a/engines/gob/game.cpp
+++ b/engines/gob/game.cpp
@@ -214,8 +214,8 @@ void Game::prepareStart() {
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
_vm->_draw->initScreen();
- _vm->_video->fillRect(*_vm->_draw->_frontSurface, 0, 0,
- _vm->_video->_surfWidth - 1, _vm->_video->_surfHeight - 1, 1);
+ _vm->_draw->_frontSurface->fillRect(0, 0,
+ _vm->_video->_surfWidth - 1, _vm->_video->_surfHeight - 1, 1);
_vm->_util->setMousePos(152, 92);
_vm->_draw->_cursorX = _vm->_global->_inter_mouseX = 152;
@@ -352,8 +352,8 @@ void Game::playTot(int16 skipPlay) {
}
}
- if (_vm->getGameType() == kGameTypeFascination)
- _vm->_draw->closeAllWin();
+ _vm->_draw->closeAllWin();
+
if (_totToLoad[0] == 0)
break;
diff --git a/engines/gob/global.h b/engines/gob/global.h
index 4fce5952eb..713b501f35 100644
--- a/engines/gob/global.h
+++ b/engines/gob/global.h
@@ -127,7 +127,7 @@ public:
bool _setAllPalette;
bool _dontSetPalette;
- SurfaceDescPtr _primarySurfDesc;
+ SurfacePtr _primarySurfDesc;
int16 _debugFlag;
int16 _inVM;
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index 03c0b1d991..65c960bd73 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -209,10 +209,6 @@ bool GobEngine::isEGA() const {
return (_features & kFeaturesEGA) != 0;
}
-bool GobEngine::is640() const {
- return (_features & kFeatures640) != 0;
-}
-
bool GobEngine::hasAdLib() const {
return (_features & kFeaturesAdLib) != 0;
}
@@ -225,22 +221,36 @@ bool GobEngine::isBATDemo() const {
return (_features & kFeaturesBATDemo) != 0;
}
+bool GobEngine::is640x480() const {
+ return (_features & kFeatures640x480) != 0;
+}
+
bool GobEngine::is800x600() const {
return (_features & kFeatures800x600) != 0;
}
+bool GobEngine::isTrueColor() const {
+ return (_features & kFeaturesTrueColor) != 0;
+}
+
bool GobEngine::isDemo() const {
return (isSCNDemo() || isBATDemo());
}
+const Graphics::PixelFormat &GobEngine::getPixelFormat() const {
+ return _pixelFormat;
+}
+
Common::Error GobEngine::run() {
if (!initGameParts()) {
GUIErrorMessage("GobEngine::init(): Unknown version of game engine");
return Common::kUnknownError;
}
- _video->setSize(is640());
- _video->init();
+ if (!initGraphics()) {
+ GUIErrorMessage("GobEngine::init(): Failed to set up graphics");
+ return Common::kUnknownError;
+ }
// On some systems it's not safe to run CD audio games from the CD.
if (isCD())
@@ -376,7 +386,7 @@ bool GobEngine::initGameParts() {
break;
case kGameTypeFascination:
- _init = new Init_v2(this);
+ _init = new Init_Fascination(this);
_video = new Video_v2(this);
_inter = new Inter_Fascination(this);
_mult = new Mult_v2(this);
@@ -518,22 +528,6 @@ bool GobEngine::initGameParts() {
_inter->setupOpcodes();
- if (is640()) {
- _video->_surfWidth = _width = 640;
- _video->_surfHeight = _video->_splitHeight1 = _height = 480;
- _global->_mouseMaxX = 640;
- _global->_mouseMaxY = 480;
- _mode = 0x18;
- _global->_primarySurfDesc = SurfaceDescPtr(new SurfaceDesc(0x18, 640, 480));
- } else {
- _video->_surfWidth = _width = 320;
- _video->_surfHeight = _video->_splitHeight1 = _height = 200;
- _global->_mouseMaxX = 320;
- _global->_mouseMaxY = 200;
- _mode = 0x14;
- _global->_primarySurfDesc = SurfaceDescPtr(new SurfaceDesc(0x14, 320, 200));
- }
-
return true;
}
@@ -556,4 +550,34 @@ void GobEngine::deinitGameParts() {
delete _dataIO; _dataIO = 0;
}
+bool GobEngine::initGraphics() {
+ if (is800x600()) {
+ warning("GobEngine::initGraphics(): 800x600 games currently unsupported");
+ return false;
+ } else if (is640x480()) {
+ _width = 640;
+ _height = 480;
+ _mode = 0x18;
+ } else {
+ _width = 320;
+ _height = 200;
+ _mode = 0x14;
+ }
+
+ _video->setSize(is640x480());
+
+ _pixelFormat = g_system->getScreenFormat();
+
+ _video->_surfWidth = _width;
+ _video->_surfHeight = _height;
+ _video->_splitHeight1 = _height;
+
+ _global->_mouseMaxX = _width;
+ _global->_mouseMaxY = _height;
+
+ _global->_primarySurfDesc = SurfacePtr(new Surface(_width, _height, _pixelFormat.bytesPerPixel));
+
+ return true;
+}
+
} // End of namespace Gob
diff --git a/engines/gob/gob.h b/engines/gob/gob.h
index dcca236ee3..f6c03fa617 100644
--- a/engines/gob/gob.h
+++ b/engines/gob/gob.h
@@ -119,14 +119,15 @@ enum GameType {
};
enum Features {
- kFeaturesNone = 0,
- kFeaturesCD = 1 << 0,
- kFeaturesEGA = 1 << 1,
- kFeaturesAdLib = 1 << 2,
- kFeatures640 = 1 << 3,
- kFeaturesSCNDemo = 1 << 4,
- kFeaturesBATDemo = 1 << 5,
- kFeatures800x600 = 1 << 6
+ kFeaturesNone = 0,
+ kFeaturesCD = 1 << 0,
+ kFeaturesEGA = 1 << 1,
+ kFeaturesAdLib = 1 << 2,
+ kFeaturesSCNDemo = 1 << 3,
+ kFeaturesBATDemo = 1 << 4,
+ kFeatures640x480 = 1 << 5,
+ kFeatures800x600 = 1 << 6,
+ kFeaturesTrueColor = 1 << 7
};
enum {
@@ -163,6 +164,8 @@ private:
bool initGameParts();
void deinitGameParts();
+ bool initGraphics();
+
public:
static const Common::Language _gobToScummVMLang[];
@@ -173,6 +176,8 @@ public:
uint16 _height;
uint8 _mode;
+ Graphics::PixelFormat _pixelFormat;
+
Common::String _startStk;
Common::String _startTot;
uint32 _demoIndex;
@@ -208,13 +213,16 @@ public:
GameType getGameType() const;
bool isCD() const;
bool isEGA() const;
- bool is640() const;
bool hasAdLib() const;
bool isSCNDemo() const;
bool isBATDemo() const;
+ bool is640x480() const;
bool is800x600() const;
+ bool isTrueColor() const;
bool isDemo() const;
+ const Graphics::PixelFormat &getPixelFormat() const;
+
GobEngine(OSystem *syst);
virtual ~GobEngine();
diff --git a/engines/gob/goblin.cpp b/engines/gob/goblin.cpp
index 11043df782..ee2b4f52c9 100644
--- a/engines/gob/goblin.cpp
+++ b/engines/gob/goblin.cpp
@@ -233,9 +233,9 @@ void Goblin::drawObjects() {
if (objDesc->toRedraw == 0)
continue;
- _vm->_video->drawSprite(*_vm->_mult->_animSurf, *_vm->_draw->_backSurface,
+ _vm->_draw->_backSurface->blit(*_vm->_mult->_animSurf,
objDesc->left, objDesc->top, objDesc->right,
- objDesc->bottom, objDesc->left, objDesc->top, 0);
+ objDesc->bottom, objDesc->left, objDesc->top);
_vm->_draw->invalidateRect(objDesc->left, objDesc->top,
objDesc->right, objDesc->bottom);
diff --git a/engines/gob/hotspots.cpp b/engines/gob/hotspots.cpp
index 1edb7fc0cb..dad141a254 100644
--- a/engines/gob/hotspots.cpp
+++ b/engines/gob/hotspots.cpp
@@ -431,9 +431,10 @@ void Hotspots::pop() {
// Find the end of the filled hotspot space
int i;
Hotspot *destPtr = _hotspots;
- for (i = 0; i < kHotspotCount; i++, destPtr++)
+ for (i = 0; i < kHotspotCount; i++, destPtr++) {
if (destPtr->isEnd())
break;
+ }
if (((uint32) (kHotspotCount - i)) < backup.size)
error("Hotspots::pop(): Not enough free space in the current Hotspot "
@@ -525,12 +526,12 @@ void Hotspots::leave(uint16 index) {
int16 Hotspots::curWindow(int16 &dx, int16 &dy) const {
if ((_vm->_draw->_renderFlags & 0x80)==0)
return(0);
- for (int i = 0; i < 10; i++)
- if (_vm->_draw->_fascinWin[i].id != -1)
+ for (int i = 0; i < 10; i++) {
+ if (_vm->_draw->_fascinWin[i].id != -1) {
if (_vm->_global->_inter_mouseX >= _vm->_draw->_fascinWin[i].left &&
_vm->_global->_inter_mouseX < _vm->_draw->_fascinWin[i].left + _vm->_draw->_fascinWin[i].width &&
_vm->_global->_inter_mouseY >= _vm->_draw->_fascinWin[i].top &&
- _vm->_global->_inter_mouseY < _vm->_draw->_fascinWin[i].top + _vm->_draw->_fascinWin[i].height)
+ _vm->_global->_inter_mouseY < _vm->_draw->_fascinWin[i].top + _vm->_draw->_fascinWin[i].height) {
if (_vm->_draw->_fascinWin[i].id == _vm->_draw->_winCount-1) {
dx = _vm->_draw->_fascinWin[i].left;
dy = _vm->_draw->_fascinWin[i].top;
@@ -546,6 +547,9 @@ int16 Hotspots::curWindow(int16 &dx, int16 &dy) const {
return(6);
return(-i);
}
+ }
+ }
+ }
return(0);
}
@@ -555,10 +559,7 @@ uint16 Hotspots::checkMouse(Type type, uint16 &id, uint16 &index) const {
int16 dx = 0;
int16 dy = 0;
- int16 winId = -1;
-
- if (_vm->getGameType() == kGameTypeFascination)
- winId = _vm->_draw->isOverWin(dx, dy);
+ int16 winId = _vm->_draw->getWinFromCoord(dx, dy);
if (winId < 0) {
winId = 0;
@@ -753,11 +754,8 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
if (_vm->_game->_mouseButtons != kMouseButtonsNone) {
// Mouse button pressed
- int i;
- if (_vm->getGameType() == kGameTypeFascination)
- i = _vm->_draw->handleCurWin();
- else
- i = 0;
+ int i = _vm->_draw->handleCurWin();
+
if (!i) {
_vm->_draw->animateCursor(2);
if (delay > 0) {
@@ -778,9 +776,11 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
((delay <= 0) || (_vm->_game->_mouseButtons == kMouseButtonsNone)))
_vm->_draw->blitCursor();
+
+ if ((key != _currentKey) && (_vm->getGameType() != kGameTypeFascination))
// If the hotspot changed, leave the old one
- if (key != _currentKey)
- leave(_currentIndex);
+ // Code not present in Fascination executables
+ leave(_currentIndex);
_currentKey = 0;
break;
@@ -800,11 +800,9 @@ uint16 Hotspots::check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index
enter(_currentIndex);
} else {
WRITE_VAR(16, (int32)i);
- if (id)
- id = 0;
- if (index)
- index = 0;
- return(0);
+ id = 0;
+ index = 0;
+ return 0;
}
} else
// No mouse button pressed, check whether the position changed at least
@@ -1232,28 +1230,28 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
height = _vm->_game->_script->readUint16();
}
if (_vm->_draw->_renderFlags & 64) {
- warning("_renderFlags check added for Fascination");
_vm->_draw->_invalidatedTops[0] = 0;
_vm->_draw->_invalidatedLefts[0] = 0;
_vm->_draw->_invalidatedRights[0] = 319;
_vm->_draw->_invalidatedBottoms[0] = 199;
_vm->_draw->_invalidatedCount = 1;
if (windowNum == 0) {
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left + width - 1, top, left + width - 1, top + height - 1, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top, left, top + height - 1, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top, left + width - 1, top, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top + height - 1, left + width - 1, top + height - 1, 0);
- } else
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left + width - 1, top, left + width - 1, top + height - 1, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top, left, top + height - 1, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top, left + width - 1, top, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top + height - 1, left + width - 1, top + height - 1, 0);
+ } else {
if ((_vm->_draw->_fascinWin[windowNum].id != -1) && (_vm->_draw->_fascinWin[windowNum].id == _vm->_draw->_winCount - 1)) {
left += _vm->_draw->_fascinWin[windowNum].left;
top += _vm->_draw->_fascinWin[windowNum].top;
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left + width - 1, top, left + width - 1, top + height - 1, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top, left, top + height - 1, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top, left + width - 1, top, 0);
- _vm->_video->drawLine(*_vm->_draw->_spritesArray[_vm->_draw->_destSurface], left, top + height - 1, left + width - 1, top + height - 1, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left + width - 1, top, left + width - 1, top + height - 1, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top, left, top + height - 1, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top, left + width - 1, top, 0);
+ _vm->_draw->_spritesArray[_vm->_draw->_destSurface]->drawLine(left, top + height - 1, left + width - 1, top + height - 1, 0);
left -= _vm->_draw->_fascinWin[windowNum].left;
top -= _vm->_draw->_fascinWin[windowNum].top;
}
+ }
}
type &= 0x7F;
@@ -1297,7 +1295,7 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
uint32 funcEnter = 0, funcLeave = 0;
if ((windowNum != 0) && (type != 0) && (type != 2))
- warning("evaluateNew - type %d, win %d\n",type, windowNum);
+ warning("evaluateNew - type %d, win %d",type, windowNum);
// Evaluate parameters for the new hotspot
switch (type) {
@@ -1393,9 +1391,9 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
ids[i] = _vm->_game->_script->readInt16();
flags = _vm->_game->_script->readInt16();
- if (flags > 3)
- warning("evaluateNew: Warning, use of type 2 or 20. flags = %d, should be %d\n", flags, flags&3);
-
+ if (flags > 3)
+ warning("evaluateNew: Warning, use of type 2 or 20. flags = %d, should be %d", flags, flags&3);
+
funcEnter = 0;
funcLeave = _vm->_game->_script->pos();
@@ -1424,7 +1422,7 @@ void Hotspots::evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
}
bool Hotspots::evaluateFind(uint16 key, int16 timeVal, const uint16 *ids,
- uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
+ uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
uint16 endIndex, int16 &duration, uint16 &id, uint16 &index, bool &finished) {
bool fascinCheck = false;
@@ -1518,7 +1516,7 @@ void Hotspots::evaluate() {
int16 duration = _vm->_game->_script->peekByte(1);
byte leaveWindowIndex = 0;
- if ( _vm->getGameType() == kGameTypeFascination )
+ if (_vm->getGameType() == kGameTypeFascination)
leaveWindowIndex = _vm->_game->_script->peekByte(2);
byte hotspotIndex1 = _vm->_game->_script->peekByte(3);
@@ -1644,7 +1642,7 @@ int16 Hotspots::findCursor(uint16 x, uint16 y) const {
int16 deltax = 0;
int16 deltay = 0;
- if ( _vm->getGameType() == kGameTypeFascination )
+ if (_vm->getGameType() == kGameTypeFascination)
cursor = curWindow(deltax, deltay);
if (cursor == 0) {
@@ -1678,7 +1676,7 @@ int16 Hotspots::findCursor(uint16 x, uint16 y) const {
cursor = 0;
for (int i = 0; (i < kHotspotCount) && !_hotspots[i].isEnd(); i++) {
const Hotspot &spot = _hotspots[i];
- // this check is /really/ Fascination specific.
+ // this check is /really/ Fascination specific.
// It's illogical, so if it's to be reused in Adi games... Be careful!
if ((spot.flags & 0xFF00) == curType)
if (spot.isIn(x - deltax, y - deltay)) {
@@ -1722,9 +1720,9 @@ void Hotspots::oPlaytoons_F_1B() {
// var_4 += unk_var;
for (int i = 0; i < kHotspotCount; i++) {
- if (_hotspots[i].isEnd()) {
+ if (_hotspots[i].isEnd())
return;
- }
+
if ((_hotspots[i].id == 0xD000 + shortId) || (_hotspots[i].id == 0xB000 + shortId) ||
(_hotspots[i].id == 0x4000 + shortId)) {
longId = _hotspots[i].id;
@@ -1745,7 +1743,6 @@ void Hotspots::oPlaytoons_F_1B() {
right -= 2;
bottom -= 2;
}
-// oPlaytoons_sub_F_1B(0x8000 + var2, left, top, right, bottom, _vm->_game->_script->getResultStr(), var3, var4, shortId);
_vm->_draw->oPlaytoons_sub_F_1B(0x8000+ var2, left, top, right, bottom, _vm->_game->_script->getResultStr(), fontIndex, var4, shortId);
return;
}
@@ -2053,7 +2050,6 @@ void Hotspots::matchInputStrings(const InputDesc *inputs) const {
cleanFloatString(spot);
if ((spot.getType() >= kTypeInput2NoLeave) && (spot.getType() <= kTypeInput3Leave)) {
-
// Look if we find a match between the wanted and the typed string
checkStringMatch(spot, inputs[inputIndex], inputPos);
strInputCount++;
@@ -2110,7 +2106,7 @@ void Hotspots::fillRect(uint16 x, uint16 y, uint16 width, uint16 height, uint16
_vm->_draw->_spriteBottom = height;
_vm->_draw->_backColor = color;
- _vm->_draw->spriteOperation(DRAW_FILLRECT | 0x10 );
+ _vm->_draw->spriteOperation(DRAW_FILLRECT | 0x10);
}
void Hotspots::printText(uint16 x, uint16 y, const char *str, uint16 fontIndex, uint16 color) const {
diff --git a/engines/gob/hotspots.h b/engines/gob/hotspots.h
index cba400d5b6..7346c66bb5 100644
--- a/engines/gob/hotspots.h
+++ b/engines/gob/hotspots.h
@@ -224,7 +224,7 @@ private:
uint16 &inputId, bool &hasInput, uint16 &inputCount);
/** Find the hotspot requested by script commands. */
bool evaluateFind(uint16 key, int16 timeVal, const uint16 *ids,
- uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
+ uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
uint16 endIndex, int16 &duration, uint16 &id, uint16 &index, bool &finished);
// Finding specific hotspots
diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp
index 3da71a2ba6..5c59a5692f 100644
--- a/engines/gob/init.cpp
+++ b/engines/gob/init.cpp
@@ -53,7 +53,6 @@ Init::~Init() {
}
void Init::cleanup() {
- _vm->_video->freeDriver();
_vm->_global->_primarySurfDesc.reset();
_vm->_sound->speakerOff();
@@ -65,8 +64,6 @@ void Init::doDemo() {
if (_vm->isSCNDemo()) {
// This is a non-interactive demo with a SCN script and VMD videos
- _vm->_video->setPrePalette();
-
SCNPlayer scnPlayer(_vm);
if (_vm->_demoIndex > 0)
diff --git a/engines/gob/init.h b/engines/gob/init.h
index d4481d8e16..8fc301d7a6 100644
--- a/engines/gob/init.h
+++ b/engines/gob/init.h
@@ -96,6 +96,7 @@ public:
Init_Fascination(GobEngine *vm);
~Init_Fascination();
+ void updateConfig();
void initGame();
};
} // End of namespace Gob
diff --git a/engines/gob/init_fascin.cpp b/engines/gob/init_fascin.cpp
new file mode 100644
index 0000000000..9842b7e752
--- /dev/null
+++ b/engines/gob/init_fascin.cpp
@@ -0,0 +1,58 @@
+/* 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/endian.h"
+
+#include "gob/gob.h"
+#include "gob/init.h"
+#include "gob/game.h"
+#include "gob/global.h"
+
+namespace Gob {
+
+Init_Fascination::Init_Fascination(GobEngine *vm) : Init_v2(vm) {
+}
+
+Init_Fascination::~Init_Fascination() {
+}
+
+void Init_Fascination::updateConfig() {
+// In Fascination, some empty texts are present and used to clean up the text area.
+// Using _doSubtitles does the trick.
+// The first obvious example is in the hotel hall: 'Use ...' is displayed at
+// the same place than the character dialogs.
+ _vm->_global->_doSubtitles = true;
+}
+
+void Init_Fascination::initGame() {
+// HACK - Suppress ADLIB_FLAG as the MDY/TBR player is not working. suppress
+// the PC Speaker too, as the script checks in the intro for it's presence
+// to play or not some noices.
+ _vm->_global->_soundFlags = MIDI_FLAG | BLASTER_FLAG;
+
+ Init::initGame();
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/init_v3.cpp b/engines/gob/init_v3.cpp
index 4a2379bad0..b3816c10e9 100644
--- a/engines/gob/init_v3.cpp
+++ b/engines/gob/init_v3.cpp
@@ -39,10 +39,11 @@ Init_v3::~Init_v3() {
}
void Init_v3::updateConfig() {
-// In the CD version of Goblins3, some texts are flagged 'subtitles'
-// incorrectly and therefore should be displayed in all cases.
+// In the CD and Windows version of Goblins3, some texts are flagged
+// 'subtitles' incorrectly and therefore should be displayed in all cases.
// The first obvious example is just after Death level.
- if ((_vm->getGameType() == kGameTypeGob3) && _vm->isCD())
+ if ((_vm->getGameType() == kGameTypeGob3) &&
+ (_vm->isCD() || (_vm->getPlatform() == Common::kPlatformWindows)))
_vm->_global->_doSubtitles = true;
}
diff --git a/engines/gob/inter.cpp b/engines/gob/inter.cpp
index 0d48ad719c..f6e6d41100 100644
--- a/engines/gob/inter.cpp
+++ b/engines/gob/inter.cpp
@@ -288,7 +288,21 @@ void Inter::funcBlock(int16 retFlag) {
_vm->_util->longDelay(5000);
}
+ } // End of workaround
+ // WORKAROUND:
+ // Apart the CD version which is playing a speech in this room, all the versions
+ // of Fascination have a too short delay between the storage room and the lab.
+ // We manually add it here.
+ if ((_vm->getGameType() == kGameTypeFascination) &&
+ !strncmp(_vm->_game->_curTotFile, "PLANQUE.tot", 9)) {
+ int addr = _vm->_game->_script->pos();
+ if ((startaddr == 0x0202 && addr == 0x0330) || // Before Lab, Amiga & Atari, English
+ (startaddr == 0x023D && addr == 0x032D) || // Before Lab, PC floppy, German
+ (startaddr == 0x02C2 && addr == 0x03C2)) { // Before Lab, PC floppy, Hebrew
+ warning("Fascination - Adding delay");
+ _vm->_util->longDelay(3000);
+ }
} // End of workaround
cmd = _vm->_game->_script->readByte();
diff --git a/engines/gob/inter.h b/engines/gob/inter.h
index 8dabe12637..4554a0783b 100644
--- a/engines/gob/inter.h
+++ b/engines/gob/inter.h
@@ -432,6 +432,8 @@ protected:
void oFascin_playProtracker(OpGobParams &params);
+ bool oFascin_repeatUntil(OpFuncParams &params);
+ bool oFascin_assign(OpFuncParams &params);
bool oFascin_copySprite(OpFuncParams &params);
bool oFascin_keyFunc(OpFuncParams &params);
@@ -452,6 +454,7 @@ protected:
void oFascin_closeWin();
void oFascin_activeWin();
void oFascin_openWin();
+ void oFascin_initCursorAnim();
void oFascin_setRenderFlags();
void oFascin_setWinFlags();
};
@@ -539,7 +542,6 @@ protected:
bool o6_loadCursor(OpFuncParams &params);
bool o6_assign(OpFuncParams &params);
- bool o6_palLoad(OpFuncParams &params);
bool o6_removeHotspot(OpFuncParams &params);
bool o6_fillRect(OpFuncParams &params);
diff --git a/engines/gob/inter_bargon.cpp b/engines/gob/inter_bargon.cpp
index 5c56196641..3afb70d6c0 100644
--- a/engines/gob/inter_bargon.cpp
+++ b/engines/gob/inter_bargon.cpp
@@ -120,16 +120,16 @@ void Inter_Bargon::oBargon_intro2(OpGobParams &params) {
int16 mouseX;
int16 mouseY;
MouseButtons buttons;
- SurfaceDescPtr surface;
+ SurfacePtr surface;
SoundDesc samples[4];
int16 comp[5] = { 0, 1, 2, 3, -1 };
static const char *sndFiles[] = {"1INTROII.snd", "2INTROII.snd", "1INTRO3.snd", "2INTRO3.snd"};
surface = _vm->_video->initSurfDesc(_vm->_global->_videoMode, 320, 200, 0);
_vm->_video->drawPackedSprite("2ille.ims", *surface);
- _vm->_video->drawSprite(*surface, *_vm->_draw->_frontSurface, 0, 0, 319, 199, 0, 0, 0);
+ _vm->_draw->_frontSurface->blit(*surface, 0, 0, 319, 199, 0, 0);
_vm->_video->drawPackedSprite("2ille4.ims", *surface);
- _vm->_video->drawSprite(*surface, *_vm->_draw->_frontSurface, 0, 0, 319, 199, 320, 0, 0);
+ _vm->_draw->_frontSurface->blit(*surface, 0, 0, 319, 199, 320, 0);
_vm->_util->setScrollOffset(320, 0);
_vm->_video->dirtyRectsAll();
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
@@ -140,7 +140,7 @@ void Inter_Bargon::oBargon_intro2(OpGobParams &params) {
if ((_vm->_game->checkKeys(&mouseX, &mouseY, &buttons, 0) == kKeyEscape) ||
_vm->shouldQuit()) {
_vm->_palAnim->fade(0, -2, 0);
- _vm->_video->clearSurf(*_vm->_draw->_frontSurface);
+ _vm->_draw->_frontSurface->clear();
memset((char *)_vm->_draw->_vgaPalette, 0, 768);
WRITE_VAR(4, buttons);
WRITE_VAR(0, kKeyEscape);
@@ -161,7 +161,7 @@ void Inter_Bargon::oBargon_intro2(OpGobParams &params) {
_vm->_sound->blasterPlayComposition(comp, 0, samples, 4);
_vm->_sound->blasterWaitEndPlay(true, false);
_vm->_palAnim->fade(0, 0, 0);
- _vm->_video->clearSurf(*_vm->_draw->_frontSurface);
+ _vm->_draw->_frontSurface->clear();
}
void Inter_Bargon::oBargon_intro3(OpGobParams &params) {
@@ -192,7 +192,7 @@ void Inter_Bargon::oBargon_intro3(OpGobParams &params) {
_vm->shouldQuit()) {
_vm->_sound->blasterStop(10);
_vm->_palAnim->fade(0, -2, 0);
- _vm->_video->clearSurf(*_vm->_draw->_frontSurface);
+ _vm->_draw->_frontSurface->clear();
memset(_vm->_draw->_vgaPalette, 0, 768);
WRITE_VAR(4, buttons);
WRITE_VAR(0, kKeyEscape);
diff --git a/engines/gob/inter_fascin.cpp b/engines/gob/inter_fascin.cpp
index 304f02f4fa..895eb85440 100644
--- a/engines/gob/inter_fascin.cpp
+++ b/engines/gob/inter_fascin.cpp
@@ -33,6 +33,7 @@
#include "gob/dataio.h"
#include "gob/draw.h"
#include "gob/game.h"
+#include "gob/expression.h"
#include "gob/script.h"
#include "gob/palanim.h"
#include "gob/video.h"
@@ -57,6 +58,8 @@ void Inter_Fascination::setupOpcodesDraw() {
OPCODEDRAW(0x05, oFascin_activeWin);
OPCODEDRAW(0x06, oFascin_openWin);
+ OPCODEDRAW(0x08, oFascin_initCursorAnim);
+
OPCODEDRAW(0x0A, oFascin_setRenderFlags);
OPCODEDRAW(0x0B, oFascin_setWinFlags);
@@ -85,7 +88,8 @@ void Inter_Fascination::setupOpcodesDraw() {
void Inter_Fascination::setupOpcodesFunc() {
Inter_v2::setupOpcodesFunc();
- OPCODEFUNC(0x09, o1_assign);
+ OPCODEFUNC(0x06, oFascin_repeatUntil);
+ OPCODEFUNC(0x09, oFascin_assign);
OPCODEFUNC(0x32, oFascin_copySprite);
}
@@ -110,6 +114,90 @@ void Inter_Fascination::setupOpcodesGob() {
OPCODEGOB(1002, o2_stopProtracker);
}
+bool Inter_Fascination::oFascin_repeatUntil(OpFuncParams &params) {
+ int16 size;
+ bool flag;
+
+ _nestLevel[0]++;
+
+ uint32 blockPos = _vm->_game->_script->pos();
+
+ do {
+ _vm->_game->_script->seek(blockPos);
+ size = _vm->_game->_script->peekUint16(2) + 2;
+
+ funcBlock(1);
+
+ _vm->_game->_script->seek(blockPos + size + 1);
+
+ flag = _vm->_game->_script->evalBoolResult();
+
+ // WORKAROUND: The script of the PC version of Fascination, when the protection check
+ // fails, writes on purpose everywhere in the memory in order to hang the computer.
+ // This results in a crash in Scummvm. This workaround avoids that crash.
+ if (_vm->getPlatform() == Common::kPlatformPC) {
+ if ((!scumm_stricmp(_vm->_game->_curTotFile, "INTRO1.TOT") && (blockPos == 3533)) ||
+ (!scumm_stricmp(_vm->_game->_curTotFile, "INTRO2.TOT") && (blockPos == 3519)) ||
+ (!scumm_stricmp(_vm->_game->_curTotFile, "INTRO2.TOT") && (blockPos == 3265))) //PC Hebrew
+ _terminate = 1;
+ }
+ } while (!flag && !_break && !_terminate && !_vm->shouldQuit());
+
+ _nestLevel[0]--;
+
+ if (*_breakFromLevel > -1) {
+ _break = false;
+ *_breakFromLevel = -1;
+ }
+ return false;
+}
+
+bool Inter_Fascination::oFascin_assign(OpFuncParams &params) {
+ byte destType = _vm->_game->_script->peekByte();
+ int16 dest = _vm->_game->_script->readVarIndex();
+
+ byte loopCount;
+ if (_vm->_game->_script->peekByte() == 99) {
+ _vm->_game->_script->skip(1);
+ loopCount = _vm->_game->_script->readByte();
+ } else
+ loopCount = 1;
+
+ for (int i = 0; i < loopCount; i++) {
+ int16 result;
+ int16 srcType = _vm->_game->_script->evalExpr(&result);
+
+ switch (destType) {
+ case TYPE_VAR_INT8:
+ if (srcType != TYPE_IMM_INT16) {
+ char* str = _vm->_game->_script->getResultStr();
+ WRITE_VARO_STR(dest, str);
+ } else
+ WRITE_VARO_UINT8(dest + i, _vm->_game->_script->getResultInt());
+ break;
+
+ case TYPE_VAR_INT32_AS_INT16:
+ case TYPE_ARRAY_INT16:
+ WRITE_VARO_UINT16(dest + i * 2, _vm->_game->_script->getResultInt());
+ break;
+
+ case TYPE_VAR_INT32:
+ case TYPE_ARRAY_INT32:
+ WRITE_VAR_OFFSET(dest + i * 4, _vm->_game->_script->getResultInt());
+ break;
+
+ case TYPE_VAR_STR:
+ case TYPE_ARRAY_STR:
+ if (srcType == TYPE_IMM_INT16)
+ WRITE_VARO_UINT8(dest, result);
+ else
+ WRITE_VARO_STR(dest, _vm->_game->_script->getResultStr());
+ break;
+ }
+ }
+
+ return false;
+}
bool Inter_Fascination::oFascin_copySprite(OpFuncParams &params) {
_vm->_draw->_sourceSurface = _vm->_game->_script->readInt16();
@@ -129,8 +217,6 @@ bool Inter_Fascination::oFascin_copySprite(OpFuncParams &params) {
}
void Inter_Fascination::oFascin_playTirb(OpGobParams &params) {
- warning("funcPlayImd with parameter : 'tirb.imd'");
-
VideoPlayer::Properties vidProps;
vidProps.type = VideoPlayer::kVideoTypePreIMD;
@@ -149,8 +235,6 @@ void Inter_Fascination::oFascin_playTirb(OpGobParams &params) {
}
void Inter_Fascination::oFascin_playTira(OpGobParams &params) {
- warning("funcPlayImd with parameter : 'tira.imd'");
-
VideoPlayer::Properties vidProps;
vidProps.type = VideoPlayer::kVideoTypePreIMD;
@@ -255,6 +339,13 @@ void Inter_Fascination::oFascin_openWin() {
WRITE_VAR((retVal / 4), (int32) _vm->_draw->openWin(id));
}
+void Inter_Fascination::oFascin_initCursorAnim() {
+ int16 ind = _vm->_game->_script->readValExpr();
+ _vm->_draw->_cursorAnimLow[ind] = _vm->_game->_script->readInt16();
+ _vm->_draw->_cursorAnimHigh[ind] = _vm->_game->_script->readInt16();
+ _vm->_draw->_cursorAnimDelays[ind] = _vm->_game->_script->readInt16();
+}
+
void Inter_Fascination::oFascin_setRenderFlags() {
int16 expr;
_vm->_game->_script->evalExpr(&expr);
@@ -270,4 +361,5 @@ void Inter_Fascination::oFascin_setWinFlags() {
void Inter_Fascination::oFascin_playProtracker(OpGobParams &params) {
_vm->_sound->protrackerPlay("mod.extasy");
}
+
} // End of namespace Gob
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 11fe0c9c5e..3bf7fc8fd4 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -492,10 +492,10 @@ void Inter_v1::o1_initMult() {
_vm->_mult->_animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
}
- _vm->_video->drawSprite(*_vm->_draw->_backSurface, *_vm->_mult->_animSurf,
+ _vm->_mult->_animSurf->blit(*_vm->_draw->_backSurface,
_vm->_mult->_animLeft, _vm->_mult->_animTop,
_vm->_mult->_animLeft + _vm->_mult->_animWidth - 1,
- _vm->_mult->_animTop + _vm->_mult->_animHeight - 1, 0, 0, 0);
+ _vm->_mult->_animTop + _vm->_mult->_animHeight - 1, 0, 0);
debugC(4, kDebugGraphics, "o1_initMult: x = %d, y = %d, w = %d, h = %d",
_vm->_mult->_animLeft, _vm->_mult->_animTop,
@@ -707,8 +707,7 @@ bool Inter_v1::o1_loadCursor(OpFuncParams &params) {
if (!resource)
return false;
- _vm->_video->fillRect(*_vm->_draw->_cursorSprites,
- index * _vm->_draw->_cursorWidth, 0,
+ _vm->_draw->_cursorSprites->fillRect(index * _vm->_draw->_cursorWidth, 0,
index * _vm->_draw->_cursorWidth + _vm->_draw->_cursorWidth - 1,
_vm->_draw->_cursorHeight - 1, 0);
@@ -1075,7 +1074,7 @@ bool Inter_v1::o1_palLoad(OpFuncParams &params) {
}
}
if (!allZero) {
- _vm->_video->clearSurf(*_vm->_draw->_frontSurface);
+ _vm->_draw->_frontSurface->clear();
_vm->_draw->_noInvalidated57 = true;
_vm->_game->_script->skip(48);
return false;
@@ -1190,6 +1189,9 @@ bool Inter_v1::o1_keyFunc(OpFuncParams &params) {
int16 key;
uint32 now;
+ _vm->_draw->forceBlit();
+ _vm->_video->retrace();
+
cmd = _vm->_game->_script->readInt16();
animPalette();
_vm->_draw->blitInvalidated();
diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp
index d0824ffb58..26eff4f675 100644
--- a/engines/gob/inter_v4.cpp
+++ b/engines/gob/inter_v4.cpp
@@ -134,8 +134,8 @@ void Inter_v4::o4_initScreen() {
_vm->_util->setScrollOffset();
if (offY > 0) {
- _vm->_draw->_spritesArray[24] = SurfaceDescPtr(new SurfaceDesc(videoMode, _vm->_width, offY));
- _vm->_draw->_spritesArray[25] = SurfaceDescPtr(new SurfaceDesc(videoMode, _vm->_width, offY));
+ _vm->_draw->_spritesArray[24] = SurfacePtr(new Surface(_vm->_width, offY, _vm->getPixelFormat().bytesPerPixel));
+ _vm->_draw->_spritesArray[25] = SurfacePtr(new Surface(_vm->_width, offY, _vm->getPixelFormat().bytesPerPixel));
_vm->_video->_splitSurf = _vm->_draw->_spritesArray[25];
}
}
@@ -228,7 +228,7 @@ void Inter_v4::o4_playVmdOrMusic() {
_vm->_vidPlayer->evaluateFlags(props);
- int slot;
+ int slot = 0;
if ((fileName[0] != 0) && ((slot = _vm->_vidPlayer->openVideo(true, fileName, props)) < 0)) {
WRITE_VAR(11, (uint32) -1);
return;
diff --git a/engines/gob/inter_v5.cpp b/engines/gob/inter_v5.cpp
index b6cfe0ea3c..a684a94a7d 100644
--- a/engines/gob/inter_v5.cpp
+++ b/engines/gob/inter_v5.cpp
@@ -194,8 +194,8 @@ void Inter_v5::o5_initScreen() {
_vm->_util->setScrollOffset();
if (offY > 0) {
- _vm->_draw->_spritesArray[24] = SurfaceDescPtr(new SurfaceDesc(videoMode, _vm->_width, offY));
- _vm->_draw->_spritesArray[25] = SurfaceDescPtr(new SurfaceDesc(videoMode, _vm->_width, offY));
+ _vm->_draw->_spritesArray[24] = SurfacePtr(new Surface(_vm->_width, offY, _vm->getPixelFormat().bytesPerPixel));
+ _vm->_draw->_spritesArray[25] = SurfacePtr(new Surface(_vm->_width, offY, _vm->getPixelFormat().bytesPerPixel));
_vm->_video->_splitSurf = _vm->_draw->_spritesArray[25];
}
}
diff --git a/engines/gob/inter_v6.cpp b/engines/gob/inter_v6.cpp
index 73ef46bf31..c6d0211c8f 100644
--- a/engines/gob/inter_v6.cpp
+++ b/engines/gob/inter_v6.cpp
@@ -65,7 +65,6 @@ void Inter_v6::setupOpcodesFunc() {
OPCODEFUNC(0x03, o6_loadCursor);
OPCODEFUNC(0x09, o6_assign);
- OPCODEFUNC(0x13, o6_palLoad);
OPCODEFUNC(0x19, o6_removeHotspot);
OPCODEFUNC(0x33, o6_fillRect);
}
@@ -171,7 +170,7 @@ void Inter_v6::o6_playVmdOrMusic() {
_vm->_vidPlayer->evaluateFlags(props);
- int slot;
+ int slot = 0;
if ((fileName[0] != 0) && ((slot = _vm->_vidPlayer->openVideo(true, fileName, props)) < 0)) {
WRITE_VAR(11, (uint32) -1);
return;
@@ -239,7 +238,7 @@ bool Inter_v6::o6_loadCursor(OpFuncParams &params) {
props.lastFrame = i;
_vm->_vidPlayer->play(vmdSlot, props);
- _vm->_vidPlayer->copyFrame(vmdSlot, _vm->_draw->_cursorSprites->getVidMem(),
+ _vm->_vidPlayer->copyFrame(vmdSlot, _vm->_draw->_cursorSprites->getData(),
0, 0, _vm->_draw->_cursorWidth, _vm->_draw->_cursorWidth,
(start + i) * _vm->_draw->_cursorWidth, 0,
_vm->_draw->_cursorSprites->getWidth());
@@ -263,8 +262,7 @@ bool Inter_v6::o6_loadCursor(OpFuncParams &params) {
if (!resource)
return false;
- _vm->_video->fillRect(*_vm->_draw->_cursorSprites,
- index * _vm->_draw->_cursorWidth, 0,
+ _vm->_draw->_cursorSprites->fillRect(index * _vm->_draw->_cursorWidth, 0,
index * _vm->_draw->_cursorWidth + _vm->_draw->_cursorWidth - 1,
_vm->_draw->_cursorHeight - 1, 0);
@@ -357,17 +355,6 @@ bool Inter_v6::o6_assign(OpFuncParams &params) {
return false;
}
-bool Inter_v6::o6_palLoad(OpFuncParams &params) {
- o1_palLoad(params);
-
- if (_gotFirstPalette)
- _vm->_video->_palLUT->setPalette((const byte *)_vm->_global->_pPaletteDesc->vgaPal,
- Graphics::PaletteLUT::kPaletteRGB, 6, 0);
-
- _gotFirstPalette = true;
- return false;
-}
-
bool Inter_v6::o6_removeHotspot(OpFuncParams &params) {
int16 id;
uint8 stateType1 = Hotspots::kStateFilledDisabled | Hotspots::kStateType1;
diff --git a/engines/gob/module.mk b/engines/gob/module.mk
index 69e7dbcf52..05658e0ca8 100644
--- a/engines/gob/module.mk
+++ b/engines/gob/module.mk
@@ -9,7 +9,6 @@ MODULE_OBJS := \
draw_bargon.o \
draw_fascin.o \
draw_playtoons.o \
- driver_vga.o \
expression.o \
game.o \
global.o \
@@ -23,6 +22,7 @@ MODULE_OBJS := \
init.o \
init_v1.o \
init_v2.o \
+ init_fascin.o \
init_v3.o \
init_v4.o \
init_v6.o \
@@ -48,6 +48,7 @@ MODULE_OBJS := \
scenery_v1.o \
scenery_v2.o \
script.o \
+ surface.o \
totfile.o \
util.o \
variables.o \
diff --git a/engines/gob/mult.h b/engines/gob/mult.h
index 7766508922..fc83e2dbe3 100644
--- a/engines/gob/mult.h
+++ b/engines/gob/mult.h
@@ -232,7 +232,7 @@ public:
int8 *_orderArray;
- SurfaceDescPtr _animSurf;
+ SurfacePtr _animSurf;
int16 _animLeft;
int16 _animTop;
int16 _animWidth;
diff --git a/engines/gob/mult_v1.cpp b/engines/gob/mult_v1.cpp
index 84869066e1..4be0a49b45 100644
--- a/engines/gob/mult_v1.cpp
+++ b/engines/gob/mult_v1.cpp
@@ -320,8 +320,7 @@ void Mult_v1::playMultInit() {
320, 200, 0);
_vm->_draw->_spritesArray[Draw::kAnimSurface] = _animSurf;
- _vm->_video->drawSprite(*_vm->_draw->_backSurface,
- *_animSurf, 0, 0, 319, 199, 0, 0, 0);
+ _animSurf->blit(*_vm->_draw->_backSurface, 0, 0, 319, 199, 0, 0);
_animDataAllocated = true;
} else
@@ -350,8 +349,7 @@ void Mult_v1::drawStatics(bool &stop) {
_vm->_scenery->_curStatic = _multData->staticIndices[_vm->_scenery->_curStatic];
_vm->_scenery->renderStatic(_vm->_scenery->_curStatic, _vm->_scenery->_curStaticLayer);
- _vm->_video->drawSprite(*_vm->_draw->_backSurface, *_animSurf,
- 0, 0, 319, 199, 0, 0, 0);
+ _animSurf->blit(*_vm->_draw->_backSurface, 0, 0, 319, 199, 0, 0);
}
}
diff --git a/engines/gob/mult_v2.cpp b/engines/gob/mult_v2.cpp
index 66488054e7..6fc292950f 100644
--- a/engines/gob/mult_v2.cpp
+++ b/engines/gob/mult_v2.cpp
@@ -272,9 +272,9 @@ void Mult_v2::loadImds(Common::SeekableReadStream &data) {
memcpy(_multData->imdFiles,
_vm->_game->_script->getData() + _vm->_game->_script->pos(), size * 14);
- // WORKAROUND: The Windows version of Lost in Time has VMD not IMD files,
- // but they are still referenced as IMD.
- if ((_vm->getGameType() == kGameTypeLostInTime) &&
+ // WORKAROUND: The Windows versions of Lost in Time and Gob3 have VMD not
+ // IMD files, but they are still referenced as IMD.
+ if (((_vm->getGameType() == kGameTypeLostInTime) || (_vm->getGameType() == kGameTypeGob3)) &&
(_vm->getPlatform() == Common::kPlatformWindows)) {
for (int i = 0; i < size; i++) {
@@ -582,9 +582,8 @@ void Mult_v2::playMultInit() {
_vm->_draw->initSpriteSurf(Draw::kAnimSurface, width, height, 0);
_animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
- _vm->_video->drawSprite(*_vm->_draw->_spritesArray[Draw::kBackSurface],
- *_vm->_draw->_spritesArray[Draw::kAnimSurface], 0, 0,
- _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0, 0);
+ _vm->_draw->_spritesArray[Draw::kAnimSurface]->blit(*_vm->_draw->_spritesArray[Draw::kBackSurface],
+ 0, 0, _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0);
for (_counter = 0; _counter < _objCount; _counter++)
_multData->palAnimIndices[_counter] = _counter;
@@ -639,9 +638,8 @@ void Mult_v2::drawStatics(bool &stop) {
_vm->_scenery->_curStatic = -1;
}
- _vm->_video->drawSprite(*_vm->_draw->_spritesArray[Draw::kBackSurface],
- *_vm->_draw->_spritesArray[Draw::kAnimSurface], 0, 0,
- _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0, 0);
+ _vm->_draw->_spritesArray[Draw::kAnimSurface]->blit(*_vm->_draw->_spritesArray[Draw::kBackSurface],
+ 0, 0, _vm->_video->_surfWidth, _vm->_video->_surfHeight, 0, 0);
}
}
@@ -946,7 +944,7 @@ void Mult_v2::animate() {
_vm->_draw->_destSpriteX = maxleft;
_vm->_draw->_destSpriteY = maxtop;
_vm->_draw->_transparency = 0;
- _vm->_draw->spriteOperation(DRAW_DRAWLETTER);
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
}
// Figure out the correct drawing order
diff --git a/engines/gob/palanim.cpp b/engines/gob/palanim.cpp
index f3770b0425..755d28c6e9 100644
--- a/engines/gob/palanim.cpp
+++ b/engines/gob/palanim.cpp
@@ -88,7 +88,7 @@ bool PalAnim::fadeStep(int16 oper) {
if (oper == 0) {
if (_vm->_global->_setAllPalette) {
if (_vm->_global->_inVM != 0)
- error("PalAnim::fadeStep(): _vm->_global->_inVM != 0 not supported.");
+ error("PalAnim::fadeStep(): _vm->_global->_inVM != 0 not supported");
for (int i = 0; i < 256; i++) {
newRed = fadeColor(_vm->_global->_redPalette[i], _toFadeRed[i]);
diff --git a/engines/gob/save/savefile.cpp b/engines/gob/save/savefile.cpp
index e1c4c62b12..79e931fe30 100644
--- a/engines/gob/save/savefile.cpp
+++ b/engines/gob/save/savefile.cpp
@@ -331,14 +331,18 @@ bool SavePartSprite::readPalette(const byte *palette) {
return true;
}
-bool SavePartSprite::readSprite(const SurfaceDesc &sprite) {
+bool SavePartSprite::readSprite(const Surface &sprite) {
// The sprite's dimensions have to fit
if (((uint32)sprite.getWidth()) != _width)
return false;
if (((uint32)sprite.getHeight()) != _height)
return false;
- memcpy(_dataSprite, sprite.getVidMem(), _width * _height);
+ // Only 8bit sprites supported for now
+ if (sprite.getBPP() != 1)
+ return false;
+
+ memcpy(_dataSprite, sprite.getData(), _width * _height);
return true;
}
@@ -357,14 +361,18 @@ bool SavePartSprite::writePalette(byte *palette) const {
return true;
}
-bool SavePartSprite::writeSprite(SurfaceDesc &sprite) const {
+bool SavePartSprite::writeSprite(Surface &sprite) const {
// The sprite's dimensions have to fit
if (((uint32)sprite.getWidth()) != _width)
return false;
if (((uint32)sprite.getHeight()) != _height)
return false;
- memcpy(sprite.getVidMem(), _dataSprite, _width * _height);
+ // Only 8bit sprites supported for now
+ if (sprite.getBPP() != 1)
+ return false;
+
+ memcpy(sprite.getData(), _dataSprite, _width * _height);
return true;
}
diff --git a/engines/gob/save/savefile.h b/engines/gob/save/savefile.h
index 615be8e0f2..da3696dee8 100644
--- a/engines/gob/save/savefile.h
+++ b/engines/gob/save/savefile.h
@@ -33,7 +33,7 @@
namespace Gob {
class GobEngine;
-class SurfaceDesc;
+class Surface;
/** A class wrapping a save part header.
*
@@ -162,7 +162,7 @@ public:
/** Read a palette into the part. */
bool readPalette(const byte *palette);
/** Read a sprite into the part. */
- bool readSprite(const SurfaceDesc &sprite);
+ bool readSprite(const Surface &sprite);
/** Read size bytes of raw data into the sprite. */
bool readSpriteRaw(const byte *data, uint32 size);
@@ -170,7 +170,7 @@ public:
/** Write a palette out of the part. */
bool writePalette(byte *palette) const;
/** Write a sprite out of the part. */
- bool writeSprite(SurfaceDesc &sprite) const;
+ bool writeSprite(Surface &sprite) const;
private:
uint32 _width;
diff --git a/engines/gob/save/savehandler.cpp b/engines/gob/save/savehandler.cpp
index 14cd82480d..8b7a661278 100644
--- a/engines/gob/save/savehandler.cpp
+++ b/engines/gob/save/savehandler.cpp
@@ -245,7 +245,7 @@ bool TempSpriteHandler::load(int16 dataVar, int32 size, int32 offset) {
if ((index < 0) || (index >= SPRITES_COUNT))
return false;
- SurfaceDescPtr sprite = _vm->_draw->_spritesArray[index];
+ SurfacePtr sprite = _vm->_draw->_spritesArray[index];
// Target sprite exists?
if (!sprite)
@@ -275,7 +275,7 @@ bool TempSpriteHandler::load(int16 dataVar, int32 size, int32 offset) {
}
bool TempSpriteHandler::save(int16 dataVar, int32 size, int32 offset) {
- SurfaceDescPtr sprite;
+ SurfacePtr sprite;
if (isDummy(size))
return true;
@@ -297,7 +297,7 @@ bool TempSpriteHandler::save(int16 dataVar, int32 size, int32 offset) {
}
bool TempSpriteHandler::createSprite(int16 dataVar, int32 size,
- int32 offset, SurfaceDescPtr *sprite) {
+ int32 offset, SurfacePtr *sprite) {
delete _sprite;
_sprite = 0;
@@ -311,7 +311,7 @@ bool TempSpriteHandler::createSprite(int16 dataVar, int32 size,
if ((index < 0) || (index >= SPRITES_COUNT))
return false;
- SurfaceDescPtr sprt = _vm->_draw->_spritesArray[index];
+ SurfacePtr sprt = _vm->_draw->_spritesArray[index];
// Sprite exists?
if (!sprt)
diff --git a/engines/gob/save/savehandler.h b/engines/gob/save/savehandler.h
index 6a7e563a8f..723215cf08 100644
--- a/engines/gob/save/savehandler.h
+++ b/engines/gob/save/savehandler.h
@@ -27,7 +27,7 @@
#define GOB_SAVE_SAVEHANDLER_H
#include "common/savefile.h"
-#include "engines/gob/video.h" // for SurfaceDescPtr
+#include "engines/gob/video.h" // for SurfacePtr
namespace Gob {
@@ -139,7 +139,7 @@ public:
/** Create a fitting sprite. */
bool createSprite(int16 dataVar, int32 size,
- int32 offset, SurfaceDescPtr *sprite = 0);
+ int32 offset, SurfacePtr *sprite = 0);
protected:
SavePartSprite *_sprite;
diff --git a/engines/gob/scenery.cpp b/engines/gob/scenery.cpp
index f9587dc0b3..ec33137739 100644
--- a/engines/gob/scenery.cpp
+++ b/engines/gob/scenery.cpp
@@ -65,7 +65,7 @@ Scenery::Scenery(GobEngine *vm) : _vm(vm) {
_pCaptureCounter = 0;
- for (int i = 0; i < 70; i++ ) {
+ for (int i = 0; i < 70; i++) {
_staticPictToSprite[i] = 0;
_animPictToSprite[i] = 0;
}
@@ -80,6 +80,10 @@ Scenery::~Scenery() {
void Scenery::init() {
for (int i = 0; i < 10; i++) {
+ if (_vm->getGameType() == kGameTypeFascination) {
+ freeAnim(i);
+ freeStatic(i);
+ }
_animPictCount[i] = 0;
_staticPictCount[i] = -1;
}
@@ -192,7 +196,7 @@ int16 Scenery::loadStatic(char search) {
_spriteResId[sprIndex] = sprResId;
_vm->_draw->initSpriteSurf(sprIndex, width, height, 2);
- _vm->_video->clearSurf(*_vm->_draw->_spritesArray[sprIndex]);
+ _vm->_draw->_spritesArray[sprIndex]->clear();
_vm->_draw->_destSurface = sprIndex;
_vm->_draw->_spriteLeft = sprResId;
_vm->_draw->_transparency = 0;
@@ -522,7 +526,7 @@ int16 Scenery::loadAnim(char search) {
_spriteResId[sprIndex] = sprResId;
_vm->_draw->initSpriteSurf(sprIndex, width, height, 2);
- _vm->_video->clearSurf(*_vm->_draw->_spritesArray[sprIndex]);
+ _vm->_draw->_spritesArray[sprIndex]->clear();
_vm->_draw->_destSurface = sprIndex;
_vm->_draw->_spriteLeft = sprResId;
_vm->_draw->_transparency = 0;
@@ -619,6 +623,16 @@ void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
if (frame >= (int32)_vm->_vidPlayer->getFrameCount(obj.videoSlot - 1))
frame = _vm->_vidPlayer->getFrameCount(obj.videoSlot - 1) - 1;
+ if (_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) >= 255) {
+ // Allow for object videos with more than 255 frames, although the
+ // object frame counter is just a byte.
+
+ uint32 curFrame = _vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1) + 1;
+ uint16 frameWrap = curFrame / 256;
+
+ frame = ((frame + 1) % 256) + frameWrap * 256;
+ }
+
if (frame != (int32)_vm->_vidPlayer->getCurrentFrame(obj.videoSlot - 1)) {
// Seek to frame
@@ -719,7 +733,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->copyFrame(obj.videoSlot - 1, _vm->_draw->_backSurface->getVidMem(),
+ _vm->_vidPlayer->copyFrame(obj.videoSlot - 1, _vm->_draw->_backSurface->getData(),
_vm->_draw->_spriteLeft, _vm->_draw->_spriteTop,
_vm->_draw->_spriteRight, _vm->_draw->_spriteBottom,
_vm->_draw->_destSpriteX, _vm->_draw->_destSpriteY,
@@ -919,13 +933,24 @@ void Scenery::writeAnimLayerInfo(uint16 index, uint16 layer,
int16 varDX, int16 varDY, int16 varUnk0, int16 varFrames) {
assert(index < 10);
- assert(layer < _animations[index].layersCount);
- AnimLayer &animLayer = _animations[index].layers[layer];
- WRITE_VAR_OFFSET(varDX, animLayer.animDeltaX);
- WRITE_VAR_OFFSET(varDY, animLayer.animDeltaY);
- WRITE_VAR_OFFSET(varUnk0, animLayer.unknown0);
- WRITE_VAR_OFFSET(varFrames, animLayer.framesCount);
+// WORKAROUND - Fascination Hebrew is using scripts from the CD versions, but of course
+// no CD track, so the anim syncing failed, and the anims were suppressed. But they
+// didn't updated the scripts. Skipping the wrong anims is a solution.
+ if ((_vm->getGameType() == kGameTypeFascination) && (layer >= _animations[index].layersCount)) {
+ WRITE_VAR_OFFSET(varDX, 0);
+ WRITE_VAR_OFFSET(varDY, 0);
+ WRITE_VAR_OFFSET(varUnk0, 0);
+ WRITE_VAR_OFFSET(varFrames, 0);
+ } else {
+ assert(layer < _animations[index].layersCount);
+
+ AnimLayer &animLayer = _animations[index].layers[layer];
+ WRITE_VAR_OFFSET(varDX, animLayer.animDeltaX);
+ WRITE_VAR_OFFSET(varDY, animLayer.animDeltaY);
+ WRITE_VAR_OFFSET(varUnk0, animLayer.unknown0);
+ WRITE_VAR_OFFSET(varFrames, animLayer.framesCount);
+ }
}
int16 Scenery::getStaticLayersCount(uint16 index) {
diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp
index f2b9004a41..bc4495fafd 100644
--- a/engines/gob/sound/sound.cpp
+++ b/engines/gob/sound/sound.cpp
@@ -610,9 +610,14 @@ void Sound::cdPlayMultMusic() {
void Sound::cdPlay(const char *trackName) {
if (!_cdrom)
return;
-
debugC(1, kDebugSound, "CDROM: Playing track \"%s\"", trackName);
- _cdrom->startTrack(trackName);
+
+// WORKAROUND - In Fascination CD, in the storage room, a track has the wrong
+// name in the scripts, and therefore doesn't play. This fixes the problem.
+ if ((_vm->getGameType() == kGameTypeFascination) && !scumm_stricmp(trackName, "boscle"))
+ _cdrom->startTrack("bosscle");
+ else
+ _cdrom->startTrack(trackName);
}
void Sound::cdStop() {
diff --git a/engines/gob/surface.cpp b/engines/gob/surface.cpp
new file mode 100644
index 0000000000..554edfc753
--- /dev/null
+++ b/engines/gob/surface.cpp
@@ -0,0 +1,584 @@
+/* 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 "gob/surface.h"
+
+#include "common/system.h"
+#include "common/util.h"
+#include "common/frac.h"
+
+#include "graphics/primitives.h"
+
+namespace Gob {
+
+static void plotPixel(int x, int y, int color, void *data) {
+ Surface *dest = (Surface *)data;
+
+ dest->putPixel(x, y, color);
+}
+
+
+Pixel::Pixel(byte *vidMem, uint8 bpp) : _vidMem(vidMem), _bpp(bpp) {
+ assert((_bpp == 1) || (_bpp == 2));
+}
+
+Pixel &Pixel::operator++() {
+ _vidMem += _bpp;
+ return *this;
+}
+
+Pixel Pixel::operator++(int x) {
+ Pixel p = *this;
+ ++(*this);
+ return p;
+}
+
+Pixel &Pixel::operator--() {
+ _vidMem -= _bpp;
+ return *this;
+}
+
+Pixel Pixel::operator--(int x) {
+ Pixel p = *this;
+ --(*this);
+ return p;
+}
+
+Pixel &Pixel::operator+=(int x) {
+ _vidMem += x * _bpp;
+ return *this;
+}
+
+Pixel &Pixel::operator-=(int x) {
+ _vidMem -= x * _bpp;
+ return *this;
+}
+
+uint32 Pixel::get() const {
+ if (_bpp == 1)
+ return *((byte *) _vidMem);
+ if (_bpp == 2)
+ return *((uint16 *) _vidMem);
+
+ return 0;
+}
+
+void Pixel::set(uint32 p) {
+ if (_bpp == 1)
+ *((byte *) _vidMem) = (byte) p;
+ if (_bpp == 2)
+ *((uint16 *) _vidMem) = (uint16) p;
+}
+
+
+ConstPixel::ConstPixel(const byte *vidMem, uint8 bpp) : _vidMem(vidMem), _bpp(bpp) {
+ assert((_bpp == 1) || (_bpp == 2));
+}
+
+ConstPixel &ConstPixel::operator++() {
+ _vidMem += _bpp;
+ return *this;
+}
+
+ConstPixel ConstPixel::operator++(int x) {
+ ConstPixel p = *this;
+ ++(*this);
+ return p;
+}
+
+ConstPixel &ConstPixel::operator--() {
+ _vidMem -= _bpp;
+ return *this;
+}
+
+ConstPixel ConstPixel::operator--(int x) {
+ ConstPixel p = *this;
+ --(*this);
+ return p;
+}
+
+ConstPixel &ConstPixel::operator+=(int x) {
+ _vidMem += x * _bpp;
+ return *this;
+}
+
+ConstPixel &ConstPixel::operator-=(int x) {
+ _vidMem -= x * _bpp;
+ return *this;
+}
+
+uint32 ConstPixel::get() const {
+ if (_bpp == 1)
+ return *((const byte *) _vidMem);
+ if (_bpp == 2)
+ return *((const uint16 *) _vidMem);
+
+ return 0;
+}
+
+
+Surface::Surface(uint16 width, uint16 height, uint8 bpp, byte *vidMem) :
+ _width(width), _height(height), _bpp(bpp), _vidMem(vidMem) {
+
+ assert((_width > 0) && (_height > 0));
+ assert((_bpp == 1) || (_bpp == 2));
+
+ if (!_vidMem) {
+ _vidMem = new byte[_bpp * _width * _height];
+ _ownVidMem = true;
+
+ memset(_vidMem, 0, _bpp * _width * _height);
+ } else
+ _ownVidMem = false;
+}
+
+Surface::~Surface() {
+ if (_ownVidMem)
+ delete[] _vidMem;
+}
+
+uint16 Surface::getWidth() const {
+ return _width;
+}
+
+uint16 Surface::getHeight() const {
+ return _height;
+}
+
+uint16 Surface::getBPP() const {
+ return _bpp;
+}
+
+void Surface::resize(uint16 width, uint16 height) {
+ assert((width > 0) && (height > 0));
+
+ if (_ownVidMem)
+ delete[] _vidMem;
+
+ _width = width;
+ _height = height;
+
+ _vidMem = new byte[_bpp * _width * _height];
+ _ownVidMem = true;
+
+ memset(_vidMem, 0, _bpp * _width * _height);
+}
+
+byte *Surface::getData(uint16 x, uint16 y) {
+ return _vidMem + (y * _width * _bpp) + (x * _bpp);
+}
+
+const byte *Surface::getData(uint16 x, uint16 y) const {
+ return _vidMem + (y * _width * _bpp) + (x * _bpp);
+}
+
+Pixel Surface::get(uint16 x, uint16 y) {
+ byte *vidMem = getData(x, y);
+
+ return Pixel(vidMem, _bpp);
+}
+
+ConstPixel Surface::get(uint16 x, uint16 y) const {
+ const byte *vidMem = getData(x, y);
+
+ return ConstPixel(vidMem, _bpp);
+}
+
+bool Surface::clipBlitRect(int16 &left, int16 &top, int16 &right, int16 &bottom, int16 &x, int16 &y,
+ uint16 dWidth, uint16 dHeight, uint16 sWidth, uint16 sHeight) {
+
+ if ((x >= dWidth) || (y >= dHeight))
+ // Nothing to do
+ return false;
+
+ // Just in case those are swapped
+ if (left > right)
+ SWAP(left, right);
+ if (top > bottom)
+ SWAP(top, bottom);
+
+ if ((left >= sWidth) || (top >= sHeight) || (right < 0) || (bottom < 0))
+ // Nothing to do
+ return false;
+
+ // Adjust from coordinates
+ if (left < 0) {
+ x -= left;
+ left = 0;
+ }
+ if (top < 0) {
+ y -= top;
+ top = 0;
+ }
+
+ // Adjust to coordinates
+ if (x < 0) {
+ left -= x;
+ x = 0;
+ }
+ if (y < 0) {
+ top -= y;
+ y = 0;
+ }
+
+ // Limit by source and destination dimensions
+ right = MIN<int32>(right , MIN<int32>(sWidth , dWidth - x + left) - 1);
+ bottom = MIN<int32>(bottom, MIN<int32>(sHeight, dHeight - y + top ) - 1);
+
+ if ((right < left) || (bottom < top))
+ // Nothing to do
+ return false;
+
+ // Clip to sane values
+ right = MAX<int16>(right , 0);
+ bottom = MAX<int16>(bottom, 0);
+
+ return true;
+}
+
+void Surface::blit(const Surface &from, int16 left, int16 top, int16 right, int16 bottom,
+ int16 x, int16 y, int32 transp) {
+
+ // Color depths have to fit
+ assert(_bpp == from._bpp);
+
+ // Clip
+ if (!clipBlitRect(left, top, right, bottom, x, y, _width, _height, from._width, from._height))
+ return;
+
+ // Area to actually copy
+ uint16 width = right - left + 1;
+ uint16 height = bottom - top + 1;
+
+ if ((width == 0) || (height == 0))
+ // Nothing to do
+ return;
+
+ if ((left == 0) && (_width == from._width) && (_width == width) && (transp == -1)) {
+ // If these conditions are met, we can directly use memcpy
+
+ // Pointers to the blit destination and source start points
+ byte *dst = getData(x , y);
+ const byte *src = from.getData(left, top);
+
+ memcpy(dst, src, width * height * _bpp);
+ return;
+ }
+
+ if (transp == -1) {
+ // We don't have to look for transparency => we can use memcpy line-wise
+
+ // Pointers to the blit destination and source start points
+ byte *dst = getData(x , y);
+ const byte *src = from.getData(left, top);
+
+ while (height-- > 0) {
+ memcpy(dst, src, width * _bpp);
+
+ dst += _width * _bpp;
+ src += from._width * from._bpp;
+ }
+
+ return;
+ }
+
+ // Otherwise, we have to copy by pixel
+
+ // Pointers to the blit destination and source start points
+ Pixel dst = get(x , y);
+ ConstPixel src = from.get(left, top);
+
+ while (height-- > 0) {
+ Pixel dstRow = dst;
+ ConstPixel srcRow = src;
+
+ for (uint16 i = 0; i < width; i++, dstRow++, srcRow++)
+ if (srcRow.get() != ((uint32) transp))
+ dstRow.set(srcRow.get());
+
+ dst += _width;
+ src += from._width;
+ }
+}
+
+void Surface::blit(const Surface &from, int16 x, int16 y, int32 transp) {
+ blit(from, 0, 0, from._width - 1, from._height - 1, x, y, transp);
+}
+
+void Surface::blit(const Surface &from, int32 transp) {
+ blit(from, 0, 0, from._width - 1, from._height - 1, 0, 0, transp);
+}
+
+void Surface::blitScaled(const Surface &from, int16 left, int16 top, int16 right, int16 bottom,
+ int16 x, int16 y, Common::Rational scale, int32 transp) {
+
+ if (scale == 1) {
+ // Yeah, "scaled"
+
+ blit(from, left, top, right, bottom, x, y, transp);
+ return;
+ }
+
+ // Color depths have to fit
+ assert(_bpp == from._bpp);
+
+ uint16 dWidth = (uint16) floor((_width / scale).toDouble());
+ uint16 dHeight = (uint16) floor((_height / scale).toDouble());
+
+ // Clip
+ if (!clipBlitRect(left, top, right, bottom, x, y, dWidth, dHeight, from._width, from._height))
+ return;
+
+ // Area to actually copy
+ uint16 width = right - left + 1;
+ uint16 height = bottom - top + 1;
+
+ if ((width == 0) || (height == 0))
+ // Nothing to do
+ return;
+
+ width = MIN<int32>((int32) floor((width * scale).toDouble()), _width);
+ height = MIN<int32>((int32) floor((height * scale).toDouble()), _height);
+
+ // Pointers to the blit destination and source start points
+ byte *dst = getData(x , y);
+ const byte *src = from.getData(left, top);
+
+ frac_t step = scale.getInverse().toFrac();
+
+ frac_t posW = 0, posH = 0;
+ while (height-- > 0) {
+ byte *dstRow = dst;
+ const byte *srcRow = src;
+
+ posW = 0;
+
+ for (uint16 i = 0; i < width; i++, dstRow += _bpp) {
+ memcpy(dstRow, srcRow, _bpp);
+
+ posW += step;
+ while (posW >= ((frac_t) FRAC_ONE)) {
+ srcRow += from._bpp;
+ posW -= FRAC_ONE;
+ }
+ }
+
+ posH += step;
+ while (posH >= ((frac_t) FRAC_ONE)) {
+ src += from._width * from._bpp;
+ posH -= FRAC_ONE;
+ }
+
+ dst += _width * _bpp;
+ }
+
+}
+
+void Surface::blitScaled(const Surface &from, int16 x, int16 y, Common::Rational scale, int32 transp) {
+ blitScaled(from, 0, 0, from._width - 1, from._height - 1, x, y, scale, transp);
+}
+
+void Surface::blitScaled(const Surface &from, Common::Rational scale, int32 transp) {
+ blitScaled(from, 0, 0, from._width - 1, from._height - 1, 0, 0, scale, transp);
+}
+
+void Surface::fillRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color) {
+ // Just in case those are swapped
+ if (left > right)
+ SWAP(left, right);
+ if (top > bottom)
+ SWAP(top, bottom);
+
+ if ((left >= _width) || (top >= _height))
+ // Nothing to do
+ return;
+
+ // Area to actually fill
+ uint16 width = CLIP<int32>(right - left + 1, 0, _width - left);
+ uint16 height = CLIP<int32>(bottom - top + 1, 0, _height - top);
+
+ if ((width == 0) || (height == 0))
+ // Nothing to do
+ return;
+
+ if ((left == 0) && (width == _width) && (_bpp == 1)) {
+ // We can directly use memset
+
+ byte *dst = getData(left, top);
+
+ memset(dst, (byte) color, width * height);
+ return;
+ }
+
+ if (_bpp == 1) {
+ // We can use memset line-wise
+
+ byte *dst = getData(left, top);
+
+ while (height-- > 0) {
+ memset(dst, (byte) color, width);
+ dst += _width;
+ }
+
+ return;
+ }
+
+ assert(_bpp == 2);
+
+ // Otherwise, we have to fill by pixel
+
+ Pixel p = get(left, top);
+ while (height-- > 0) {
+ for (uint16 i = 0; i < width; i++, ++p)
+ p.set(color);
+
+ p += _width - width;
+ }
+}
+
+void Surface::fill(uint32 color) {
+ if (_bpp == 1) {
+ // We can directly use memset
+
+ memset(_vidMem, (byte) color, _width * _height);
+ return;
+ }
+
+ fillRect(0, 0, _width - 1, _height - 1, color);
+}
+
+void Surface::clear() {
+ fill(0);
+}
+
+void Surface::putPixel(uint16 x, uint16 y, uint32 color) {
+ if ((x >= _width) || (y >= _height))
+ return;
+
+ get(x, y).set(color);
+}
+
+void Surface::drawLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, uint32 color) {
+ Graphics::drawLine(x0, y0, x1, y1, color, &plotPixel, this);
+}
+
+/*
+ * The original's version of the Bresenham Algorithm was a bit "unclean"
+ * and produced strange edges at 45, 135, 225 and 315 degrees, so using the
+ * version found in the Wikipedia article about the
+ * "Bresenham's line algorithm" instead
+ */
+void Surface::drawCircle(uint16 x0, uint16 y0, uint16 radius, uint32 color, int16 pattern) {
+ int16 f = 1 - radius;
+ int16 ddFx = 0;
+ int16 ddFy = -2 * radius;
+ int16 x = 0;
+ int16 y = radius;
+
+ if (pattern == 0) {
+ putPixel(x0, y0 + radius, color);
+ putPixel(x0, y0 - radius, color);
+ putPixel(x0 + radius, y0, color);
+ putPixel(x0 - radius, y0, color);
+ } else
+ warning("Surface::drawCircle - pattern %d", pattern);
+
+ while (x < y) {
+ if (f >= 0) {
+ y--;
+ ddFy += 2;
+ f += ddFy;
+ }
+ x++;
+ ddFx += 2;
+ f += ddFx + 1;
+
+ switch (pattern) {
+ case -1:
+ fillRect(x0 - y, y0 + x, x0 + y, y0 + x, color);
+ fillRect(x0 - x, y0 + y, x0 + x, y0 + y, color);
+ fillRect(x0 - y, y0 - x, x0 + y, y0 - x, color);
+ fillRect(x0 - x, y0 - y, x0 + x, y0 - y, color);
+ break;
+ case 0:
+ putPixel(x0 + x, y0 + y, color);
+ putPixel(x0 - x, y0 + y, color);
+ putPixel(x0 + x, y0 - y, color);
+ putPixel(x0 - x, y0 - y, color);
+ putPixel(x0 + y, y0 + x, color);
+ putPixel(x0 - y, y0 + x, color);
+ putPixel(x0 + y, y0 - x, color);
+ putPixel(x0 - y, y0 - x, color);
+ break;
+ default:
+ fillRect(x0 + y - pattern, y0 + x - pattern, x0 + y, y0 + x, color);
+ fillRect(x0 + x - pattern, y0 + y - pattern, x0 + x, y0 + y, color);
+ fillRect(x0 - y, y0 + x - pattern, x0 - y + pattern, y0 + x, color);
+ fillRect(x0 - x, y0 + y - pattern, x0 - x + pattern, y0 + y, color);
+ fillRect(x0 + y - pattern, y0 - x, x0 + y, y0 - x + pattern, color);
+ fillRect(x0 + x - pattern, y0 - y, x0 + x, y0 - y + pattern, color);
+ fillRect(x0 - y, y0 - x, x0 - y + pattern, y0 - x + pattern, color);
+ fillRect(x0 - x, y0 - y, x0 - x + pattern, y0 - y + pattern, color);
+ break;
+ }
+ }
+}
+
+void Surface::blitToScreen(uint16 left, uint16 top, uint16 right, uint16 bottom, uint16 x, uint16 y) const {
+ // Color depths have to fit
+ assert(g_system->getScreenFormat().bytesPerPixel == _bpp);
+
+ uint16 sWidth = g_system->getWidth();
+ uint16 sHeight = g_system->getHeight();
+
+ if ((x >= sWidth) || (y >= sHeight))
+ // Nothing to do
+ return;
+
+ // Just in case those are swapped
+ if (left > right)
+ SWAP(left, right);
+ if (top > bottom)
+ SWAP(top, bottom);
+
+ if ((left >= _width) || (top >= _height))
+ // Nothing to do
+ return;
+
+ // Area to actually copy
+ uint16 width = MAX<int32>(MIN<int32>(MIN<int32>(right - left + 1, _width - left), sWidth - x), 0);
+ uint16 height = MAX<int32>(MIN<int32>(MIN<int32>(bottom - top + 1, _height - top ), sHeight - y), 0);
+
+ if ((width == 0) || (height == 0))
+ // Nothing to do
+ return;
+
+ // Pointers to the blit destination and source start points
+ const byte *src = getData(left, top);
+
+ g_system->copyRectToScreen(src, _width * _bpp, x, y, width, height);
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/surface.h b/engines/gob/surface.h
new file mode 100644
index 0000000000..9a26dbc79b
--- /dev/null
+++ b/engines/gob/surface.h
@@ -0,0 +1,131 @@
+/* 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 GOB_SURFACE_H
+#define GOB_SURFACE_H
+
+#include "common/scummsys.h"
+#include "common/ptr.h"
+#include "common/rational.h"
+
+namespace Gob {
+
+/** An iterator over a surface's image data, automatically handles different color depths. */
+class Pixel {
+public:
+ Pixel(byte *vidMem, uint8 bpp);
+
+ Pixel &operator++();
+ Pixel operator++(int x);
+
+ Pixel &operator--();
+ Pixel operator--(int x);
+
+ Pixel &operator+=(int x);
+ Pixel &operator-=(int x);
+
+ uint32 get() const;
+ void set(uint32 p);
+
+private:
+ byte *_vidMem;
+ uint8 _bpp;
+};
+
+/** A const iterator over a surface's image data, automatically handles different color depths. */
+class ConstPixel {
+public:
+ ConstPixel(const byte *vidMem, uint8 bpp);
+
+ ConstPixel &operator++();
+ ConstPixel operator++(int x);
+
+ ConstPixel &operator--();
+ ConstPixel operator--(int x);
+
+ ConstPixel &operator+=(int x);
+ ConstPixel &operator-=(int x);
+
+ uint32 get() const;
+
+private:
+ const byte *_vidMem;
+ uint8 _bpp;
+};
+
+class Surface {
+public:
+ Surface(uint16 width, uint16 height, uint8 bpp, byte *vidMem = 0);
+ ~Surface();
+
+ uint16 getWidth () const;
+ uint16 getHeight() const;
+ uint16 getBPP () const;
+
+ byte *getData(uint16 x = 0, uint16 y = 0);
+ const byte *getData(uint16 x = 0, uint16 y = 0) const;
+
+ void resize(uint16 width, uint16 height);
+
+ Pixel get(uint16 x = 0, uint16 y = 0);
+ ConstPixel get(uint16 x = 0, uint16 y = 0) const;
+
+ void blit(const Surface &from, int16 left, int16 top, int16 right, int16 bottom,
+ int16 x, int16 y, int32 transp = -1);
+ void blit(const Surface &from, int16 x, int16 y, int32 transp = -1);
+ void blit(const Surface &from, int32 transp = -1);
+
+ void blitScaled(const Surface &from, int16 left, int16 top, int16 right, int16 bottom,
+ int16 x, int16 y, Common::Rational scale, int32 transp = -1);
+ void blitScaled(const Surface &from, int16 x, int16 y, Common::Rational scale, int32 transp = -1);
+ void blitScaled(const Surface &from, Common::Rational scale, int32 transp = -1);
+
+ void fillRect(uint16 left, uint16 top, uint16 right, uint16 bottom, uint32 color);
+ void fill(uint32 color);
+ void clear();
+
+ void putPixel(uint16 x, uint16 y, uint32 color);
+ void drawLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, uint32 color);
+ void drawCircle(uint16 x0, uint16 y0, uint16 radius, uint32 color, int16 pattern = 0);
+
+ void blitToScreen(uint16 left, uint16 top, uint16 right, uint16 bottom, uint16 x, uint16 y) const;
+
+private:
+ uint16 _width;
+ uint16 _height;
+ uint8 _bpp;
+
+ bool _ownVidMem;
+ byte *_vidMem;
+
+ static bool clipBlitRect(int16 &left, int16 &top, int16 &right, int16 &bottom, int16 &x, int16 &y,
+ uint16 dWidth, uint16 dHeight, uint16 sWidth, uint16 sHeight);
+};
+
+typedef Common::SharedPtr<Surface> SurfacePtr;
+
+} // End of namespace Gob
+
+#endif // GOB_SURFACE_H
diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp
index 26899232e2..00d8c2c9ac 100644
--- a/engines/gob/util.cpp
+++ b/engines/gob/util.cpp
@@ -316,8 +316,11 @@ void Util::clearPalette() {
_vm->validateVideoMode(_vm->_global->_videoMode);
if (_vm->_global->_setAllPalette) {
- memset(colors, 0, 1024);
- g_system->setPalette(colors, 0, 256);
+ if (_vm->getPixelFormat().bytesPerPixel == 1) {
+ memset(colors, 0, 1024);
+ g_system->setPalette(colors, 0, 256);
+ }
+
return;
}
@@ -435,7 +438,7 @@ void Util::cleanupStr(char *str) {
cutFromStr(str, 0, 1);
// Trim spaces right
- while ((strlen(str) > 0) && (str[strlen(str) - 1] == ' '))
+ while ((*str != '\0') && (str[strlen(str) - 1] == ' '))
cutFromStr(str, strlen(str) - 1, 1);
// Merge double spaces
diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp
index e2c25c3a22..ee73f14dfa 100644
--- a/engines/gob/video.cpp
+++ b/engines/gob/video.cpp
@@ -30,7 +30,6 @@
#include "graphics/cursorman.h"
#include "graphics/fontman.h"
#include "graphics/surface.h"
-#include "graphics/dither.h"
#include "gob/gob.h"
#include "gob/video.h"
@@ -39,8 +38,6 @@
#include "gob/dataio.h"
#include "gob/draw.h"
-#include "gob/driver_vga.h"
-
namespace Gob {
Font::Font(const byte *data) : _dataPtr(data) {
@@ -90,84 +87,68 @@ uint16 Font::getCharCount() const {
return _endItem - _startItem + 1;
}
-uint8 Font::getFirstChar() const {
- return _startItem;
-}
-
-uint8 Font::getLastChar() const {
- return _endItem;
-}
-
-uint8 Font::getCharSize() const {
- return _itemSize;
-}
-
bool Font::isMonospaced() const {
return _charWidths == 0;
}
-const byte *Font::getCharData(uint8 c) const {
- if (_endItem == 0) {
- warning("Font::getCharData(): _endItem == 0");
- return 0;
+void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,
+ uint32 color1, uint32 color2, bool transp) const {
+
+ uint16 data;
+
+ const byte *src = getCharData(c);
+ if (!src) {
+ warning("Font::drawLetter(): getCharData() == 0");
+ return;
}
- if ((c < _startItem) || (c > _endItem))
- return 0;
+ Pixel dst = surf.get(x, y);
- return _data + (c - _startItem) * _itemSize;
-}
+ int nWidth = _itemWidth;
+ if (nWidth & 7)
+ nWidth = (nWidth & 0xF8) + 8;
+ nWidth >>= 3;
-SurfaceDesc::SurfaceDesc(int16 vidMode, int16 width, int16 height,
- byte *vidMem) : _width(width), _height(height) {
+ for (int i = 0; i < _itemHeight; i++) {
+ int width = _itemWidth;
- if (vidMem) {
- _vidMode = vidMode;
- _ownVidMem = false;
- _vidMem = vidMem;
- } else {
- _vidMode = vidMode;
- _ownVidMem = true;
- _vidMem = new byte[width * height];
- assert(_vidMem);
- memset(_vidMem, 0, width * height);
- }
-}
+ for (int k = 0; k < nWidth; k++) {
-void SurfaceDesc::setVidMem(byte *vidMem) {
- assert(vidMem);
+ data = *src++;
+ for (int j = 0; j < MIN(8, width); j++) {
+ if (data & 0x80)
+ dst.set(color1);
+ else if (!transp)
+ dst.set(color2);
- if (hasOwnVidMem())
- delete[] _vidMem;
+ dst++;
+ data <<= 1;
+ }
- _ownVidMem = false;
- _vidMem = vidMem;
-}
+ width -= 8;
-void SurfaceDesc::resize(int16 width, int16 height) {
- if (hasOwnVidMem())
- delete[] _vidMem;
+ }
- _width = width;
- _height = height;
- _ownVidMem = true;
- _vidMem = new byte[width * height];
- assert(_vidMem);
- memset(_vidMem, 0, width * height);
+ dst += surf.getWidth() - _itemWidth;
+ }
}
-void SurfaceDesc::swap(SurfaceDesc &surf) {
- SWAP(_width, surf._width);
- SWAP(_height, surf._height);
- SWAP(_vidMode, surf._vidMode);
- SWAP(_ownVidMem, surf._ownVidMem);
- SWAP(_vidMem, surf._vidMem);
+const byte *Font::getCharData(uint8 c) const {
+ if (_endItem == 0) {
+ warning("Font::getCharData(): _endItem == 0");
+ return 0;
+ }
+
+ if ((c < _startItem) || (c > _endItem))
+ return 0;
+
+ return _data + (c - _startItem) * _itemSize;
}
+
Video::Video(GobEngine *vm) : _vm(vm) {
_doRangeClamp = false;
- _videoDriver = 0;
_surfWidth = 320;
_surfHeight = 200;
@@ -186,24 +167,9 @@ Video::Video(GobEngine *vm) : _vm(vm) {
_lastSparse = 0xFFFFFFFF;
_dirtyAll = false;
-
- _palLUT = new Graphics::PaletteLUT(5, Graphics::PaletteLUT::kPaletteYUV);
-}
-
-char Video::initDriver(int16 vidMode) {
- if (_videoDriver)
- return 1;
-
- _videoDriver = new VGAVideoDriver();
- return 1;
}
Video::~Video() {
- delete _palLUT;
-}
-
-void Video::freeDriver() {
- delete _videoDriver;
}
void Video::initPrimary(int16 mode) {
@@ -215,9 +181,6 @@ void Video::initPrimary(int16 mode) {
mode = 3;
_vm->_global->_oldMode = mode;
- if (mode != 3)
- Video::initDriver(mode);
-
if (mode != 3) {
initSurfDesc(mode, _surfWidth, _surfHeight, PRIMARY_SURFACE);
@@ -226,8 +189,8 @@ void Video::initPrimary(int16 mode) {
}
}
-SurfaceDescPtr Video::initSurfDesc(int16 vidMode, int16 width, int16 height, int16 flags) {
- SurfaceDescPtr descPtr;
+SurfacePtr Video::initSurfDesc(int16 vidMode, int16 width, int16 height, int16 flags) {
+ SurfacePtr descPtr;
if (flags & PRIMARY_SURFACE)
assert((width == _surfWidth) && (height == _surfHeight));
@@ -240,14 +203,13 @@ SurfaceDescPtr Video::initSurfDesc(int16 vidMode, int16 width, int16 height, int
descPtr = _vm->_global->_primarySurfDesc;
descPtr->resize(width, height);
- descPtr->_vidMode = vidMode;
} else {
assert(!(flags & DISABLE_SPR_ALLOC));
if (!(flags & SCUMMVM_CURSOR))
width = (width + 7) & 0xFFF8;
- descPtr = SurfaceDescPtr(new SurfaceDesc(vidMode, width, height));
+ descPtr = SurfacePtr(new Surface(width, height, _vm->getPixelFormat().bytesPerPixel));
}
return descPtr;
}
@@ -257,14 +219,16 @@ void Video::clearScreen() {
}
void Video::setSize(bool defaultTo1XScaler) {
- initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler);
+ if (_vm->isTrueColor())
+ initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler, 0);
+ else
+ initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler);
}
void Video::retrace(bool mouse) {
if (mouse)
CursorMan.showMouse((_vm->_draw->_showCursor & 2) != 0);
if (_vm->_global->_primarySurfDesc) {
- int screenOffset = _scrollOffsetY * _surfWidth + _scrollOffsetX;
int screenX = _screenDeltaX;
int screenY = _screenDeltaY;
int screenWidth = MIN<int>(_surfWidth - _scrollOffsetX, _vm->_width);
@@ -275,18 +239,15 @@ void Video::retrace(bool mouse) {
if (_splitSurf) {
- screenOffset = 0;
screenX = 0;
screenY = _vm->_height - _splitSurf->getHeight();
screenWidth = MIN<int>(_vm->_width, _splitSurf->getWidth());
screenHeight = _splitSurf->getHeight();
- g_system->copyRectToScreen(_splitSurf->getVidMem() + screenOffset,
- _splitSurf->getWidth(), screenX, screenY, screenWidth, screenHeight);
+ _splitSurf->blitToScreen(0, 0, screenWidth - 1, screenHeight - 1, screenX, screenY);
} else if (_splitHeight2 > 0) {
- screenOffset = _splitStart * _surfWidth;
screenX = 0;
screenY = _vm->_height - _splitHeight2;
screenWidth = MIN<int>(_surfWidth, _vm->_width);
@@ -318,191 +279,58 @@ void Video::sparseRetrace(int max) {
_lastSparse = timeKey;
}
-void Video::putPixel(int16 x, int16 y, int16 color, SurfaceDesc &dest) {
- if ((x >= dest.getWidth()) || (x < 0) ||
- (y >= dest.getHeight()) || (y < 0))
- return;
+void Video::drawPacked(byte *sprBuf, int16 width, int16 height,
+ int16 x, int16 y, byte transp, Surface &dest) {
- _videoDriver->putPixel(x, y, color, dest);
-}
-
-void Video::fillRect(SurfaceDesc &dest, int16 left, int16 top, int16 right,
- int16 bottom, int16 color) {
-
- if (_doRangeClamp) {
- if (left > right)
- SWAP(left, right);
- if (top > bottom)
- SWAP(top, bottom);
+ int destRight = x + width;
+ int destBottom = y + height;
- if ((left >= dest.getWidth()) || (right < 0) ||
- (top >= dest.getHeight()) || (bottom < 0))
- return;
+ Pixel dst = dest.get(x, y);
- left = CLIP(left, (int16)0, (int16)(dest.getWidth() - 1));
- top = CLIP(top, (int16)0, (int16)(dest.getHeight() - 1));
- right = CLIP(right, (int16)0, (int16)(dest.getWidth() - 1));
- bottom = CLIP(bottom, (int16)0, (int16)(dest.getHeight() - 1));
- }
-
- _videoDriver->fillRect(dest, left, top, right, bottom, color);
-}
+ int curx = x;
+ int cury = y;
-void Video::drawLine(SurfaceDesc &dest, int16 x0, int16 y0, int16 x1,
- int16 y1, int16 color) {
-
- if ((x0 == x1) || (y0 == y1))
- Video::fillRect(dest, x0, y0, x1, y1, color);
- else
- _videoDriver->drawLine(dest, x0, y0, x1, y1, color);
-}
-
-/*
- * The original's version of the Bresenham Algorithm was a bit "unclean"
- * and produced strange edges at 45, 135, 225 and 315 degrees, so using the
- * version found in the Wikipedia article about the
- * "Bresenham's line algorithm" instead
- */
-void Video::drawCircle(SurfaceDesc &dest, int16 x0, int16 y0,
- int16 radius, int16 color) {
- int16 f = 1 - radius;
- int16 ddFx = 0;
- int16 ddFy = -2 * radius;
- int16 x = 0;
- int16 y = radius;
- int16 tmpPattern = (_vm->_draw->_pattern & 0xFF);
-
- if (tmpPattern == 0) {
- putPixel(x0, y0 + radius, color, dest);
- putPixel(x0, y0 - radius, color, dest);
- putPixel(x0 + radius, y0, color, dest);
- putPixel(x0 - radius, y0, color, dest);
- } else
- warning ("Video::drawCircle - pattern %d", _vm->_draw->_pattern);
+ while (1) {
+ uint8 val = *sprBuf++;
+ unsigned int repeat = val & 7;
+ val &= 0xF8;
- while (x < y) {
- if (f >= 0) {
- y--;
- ddFy += 2;
- f += ddFy;
+ if (!(val & 8)) {
+ repeat <<= 8;
+ repeat |= *sprBuf++;
}
- x++;
- ddFx += 2;
- f += ddFx + 1;
-
- switch (tmpPattern) {
- case -1:
- fillRect(dest, x0 - y, y0 + x, x0 + y, y0 + x, color);
- fillRect(dest, x0 - x, y0 + y, x0 + x, y0 + y, color);
- fillRect(dest, x0 - y, y0 - x, x0 + y, y0 - x, color);
- fillRect(dest, x0 - x, y0 - y, x0 + x, y0 - y, color);
- break;
- case 0:
- putPixel(x0 + x, y0 + y, color, dest);
- putPixel(x0 - x, y0 + y, color, dest);
- putPixel(x0 + x, y0 - y, color, dest);
- putPixel(x0 - x, y0 - y, color, dest);
- putPixel(x0 + y, y0 + x, color, dest);
- putPixel(x0 - y, y0 + x, color, dest);
- putPixel(x0 + y, y0 - x, color, dest);
- putPixel(x0 - y, y0 - x, color, dest);
- break;
- default:
- fillRect(dest, x0 + y - tmpPattern, y0 + x - tmpPattern, x0 + y, y0 + x, color);
- fillRect(dest, x0 + x - tmpPattern, y0 + y - tmpPattern, x0 + x, y0 + y, color);
- fillRect(dest, x0 - y, y0 + x - tmpPattern, x0 - y + tmpPattern, y0 + x, color);
- fillRect(dest, x0 - x, y0 + y - tmpPattern, x0 - x + tmpPattern, y0 + y, color);
- fillRect(dest, x0 + y - tmpPattern, y0 - x, x0 + y, y0 - x + tmpPattern, color);
- fillRect(dest, x0 + x - tmpPattern, y0 - y, x0 + x, y0 - y + tmpPattern, color);
- fillRect(dest, x0 - y, y0 - x, x0 - y + tmpPattern, y0 - x + tmpPattern, color);
- fillRect(dest, x0 - x, y0 - y, x0 - x + tmpPattern, y0 - y + tmpPattern, color);
- break;
+ repeat++;
+ val >>= 4;
+
+ for (unsigned int i = 0; i < repeat; ++i) {
+ if (curx < dest.getWidth() && cury < dest.getHeight())
+ if (!transp || val)
+ dst.set(val);
+
+ dst++;
+ curx++;
+ if (curx == destRight) {
+ dst += dest.getWidth() + x - curx;
+ curx = x;
+ cury++;
+ if (cury == destBottom)
+ return;
+ }
}
- }
-}
-
-void Video::clearSurf(SurfaceDesc &dest) {
- Video::fillRect(dest, 0, 0, dest.getWidth() - 1, dest.getHeight() - 1, 0);
-}
-
-void Video::drawSprite(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) {
- int16 destRight;
- int16 destBottom;
- if (_doRangeClamp) {
- if (left > right)
- SWAP(left, right);
- if (top > bottom)
- SWAP(top, bottom);
-
- if ((left >= source.getWidth()) || (right < 0) ||
- (top >= source.getHeight()) || (bottom < 0))
- return;
-
- if (left < 0) {
- x -= left;
- left = 0;
- }
- if (top < 0) {
- y -= top;
- top = 0;
- }
- right = CLIP(right, (int16)0, (int16)(source.getWidth() - 1));
- bottom = CLIP(bottom, (int16)0, (int16)(source.getHeight() - 1));
- if (right - left >= source.getWidth())
- right = left + source.getWidth() - 1;
- if (bottom - top >= source.getHeight())
- bottom = top + source.getHeight() - 1;
-
- if (x < 0) {
- left -= x;
- x = 0;
- }
- if (y < 0) {
- top -= y;
- y = 0;
- }
- if ((x >= dest.getWidth()) || (left > right) ||
- (y >= dest.getHeight()) || (top > bottom))
- return;
-
- destRight = x + right - left;
- destBottom = y + bottom - top;
- if (destRight >= dest.getWidth())
- right -= destRight - dest.getWidth() + 1;
-
- if (destBottom >= dest.getHeight())
- bottom -= destBottom - dest.getHeight() + 1;
}
-
- _videoDriver->drawSprite(source, dest, left, top, right, bottom, x, y, transp);
-}
-
-void Video::drawSpriteDouble(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) {
-
- _videoDriver->drawSpriteDouble(source, dest, left, top, right, bottom, x, y, transp);
-}
-
-void Video::drawLetter(int16 item, int16 x, int16 y, const Font &font,
- int16 color1, int16 color2, int16 transp, SurfaceDesc &dest) {
- assert(item != 0x00);
- _videoDriver->drawLetter((unsigned char)item, x, y, font, color1, color2, transp, dest);
}
void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height,
- int16 x, int16 y, int16 transp, SurfaceDesc &dest) {
+ int16 x, int16 y, int16 transp, Surface &dest) {
if (spriteUncompressor(sprBuf, width, height, x, y, transp, dest))
return;
- _vm->validateVideoMode(dest._vidMode);
-
- _videoDriver->drawPackedSprite(sprBuf, width, height, x, y, transp, dest);
+ drawPacked(sprBuf, width, height, x, y, transp, dest);
}
-void Video::drawPackedSprite(const char *path, SurfaceDesc &dest, int width) {
+void Video::drawPackedSprite(const char *path, Surface &dest, int width) {
byte *data;
data = _vm->_dataIO->getData(path);
@@ -521,7 +349,8 @@ void Video::setPalElem(int16 index, char red, char green, char blue,
_vm->_global->_bluePalette[index] = blue;
setPalColor(pal, red, green, blue);
- g_system->setPalette(pal, index, 1);
+ if (_vm->getPixelFormat().bytesPerPixel == 1)
+ g_system->setPalette(pal, index, 1);
}
void Video::setPalette(PalDesc *palDesc) {
@@ -534,7 +363,8 @@ void Video::setPalette(PalDesc *palDesc) {
for (int i = 0; i < numcolors; i++)
setPalColor(pal + i * 4, palDesc->vgaPal[i]);
- g_system->setPalette(pal, 0, numcolors);
+ if (_vm->getPixelFormat().bytesPerPixel == 1)
+ g_system->setPalette(pal, 0, numcolors);
}
void Video::setFullPalette(PalDesc *palDesc) {
@@ -549,7 +379,8 @@ void Video::setFullPalette(PalDesc *palDesc) {
setPalColor(pal + i * 4, colors[i]);
}
- g_system->setPalette(pal, 0, 256);
+ if (_vm->getPixelFormat().bytesPerPixel == 1)
+ g_system->setPalette(pal, 0, 256);
} else
Video::setPalette(palDesc);
}
@@ -587,61 +418,26 @@ void Video::dirtyRectsAdd(int16 left, int16 top, int16 right, int16 bottom) {
}
void Video::dirtyRectsApply(int left, int top, int width, int height, int x, int y) {
- byte *vidMem = _vm->_global->_primarySurfDesc->getVidMem();
-
if (_dirtyAll) {
- g_system->copyRectToScreen(vidMem + top * _surfWidth + left,
- _surfWidth, x, y, width, height);
+ _vm->_global->_primarySurfDesc->blitToScreen(left, top, left + width - 1, top + height - 1, x, y);
return;
}
- int right = left + width;
- int bottom = top + height;
+ int right = left + width;
+ int bottom = top + height;
Common::List<Common::Rect>::const_iterator it;
for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
- int l = MAX<int>(left, it->left);
- int t = MAX<int>(top, it->top);
- int r = MIN<int>(right, it->right);
+ int l = MAX<int>(left , it->left);
+ int t = MAX<int>(top , it->top);
+ int r = MIN<int>(right , it->right);
int b = MIN<int>(bottom, it->bottom);
- int w = r - l;
- int h = b - t;
- if ((w <= 0) || (h <= 0))
+ if ((r <= l) || (b <= t))
continue;
- byte *v = vidMem + t * _surfWidth + l;
-
- g_system->copyRectToScreen(v, _surfWidth, x + (l - left), y + (t - top), w, h);
+ _vm->_global->_primarySurfDesc->blitToScreen(l, t, r - 1, b - 1, x + (l - left), y + (t - top));
}
}
-void Video::initOSD() {
- const byte palOSD[] = {
- 0, 0, 0, 0,
- 0, 0, 171, 0,
- 0, 171, 0, 0,
- 0, 171, 171, 0,
- 171, 0, 0, 0
- };
-
- g_system->setPalette(palOSD, 0, 5);
-}
-
-void Video::drawOSDText(const char *text) {
- const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kOSDFont));
- uint32 color = 0x2;
- Graphics::Surface surf;
-
- surf.create(g_system->getWidth(), font.getFontHeight(), 1);
-
- font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
-
- int y = g_system->getHeight() / 2 - font.getFontHeight() / 2;
- g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
- g_system->updateScreen();
-
- surf.free();
-}
-
} // End of namespace Gob
diff --git a/engines/gob/video.h b/engines/gob/video.h
index b8a46598b7..5c49042496 100644
--- a/engines/gob/video.h
+++ b/engines/gob/video.h
@@ -31,29 +31,23 @@
#include "common/ptr.h"
#include "gob/gob.h"
-
-namespace Graphics {
- class PaletteLUT;
-}
+#include "gob/surface.h"
namespace Gob {
class Font {
public:
+ Font(const byte *data);
+ ~Font();
+
uint8 getCharWidth (uint8 c) const;
uint8 getCharWidth () const;
uint8 getCharHeight() const;
- uint16 getCharCount () const;
- uint8 getFirstChar () const;
- uint8 getLastChar () const;
- uint8 getCharSize () const;
bool isMonospaced() const;
- const byte *getCharData(uint8 c) const;
-
- Font(const byte *data);
- ~Font();
+ void drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,
+ uint32 color1, uint32 color2, bool transp) const;
private:
const byte *_dataPtr;
@@ -66,46 +60,19 @@ private:
uint8 _endItem;
int8 _itemSize;
int8 _bitWidth;
-};
-
-// Some Surfaces are simultaneous in Draw::spritesArray and discrete
-// variables, so if in doubt you should use a SurfaceDescPtr shared
-// pointer object to refer to any SurfaceDesc.
-class SurfaceDesc {
-public:
- int16 _vidMode;
-
- int16 getWidth() const { return _width; }
- int16 getHeight() const { return _height; }
- byte *getVidMem() { return _vidMem; }
- const byte *getVidMem() const { return _vidMem; }
- bool hasOwnVidMem() const { return _ownVidMem; }
- void setVidMem(byte *vidMem);
- void resize(int16 width, int16 height);
- void swap(SurfaceDesc &surf);
-
- SurfaceDesc(int16 vidMode, int16 width, int16 height, byte *vidMem = 0);
- ~SurfaceDesc() { if (_ownVidMem) delete[] _vidMem; }
-
-private:
- int16 _width;
- int16 _height;
- byte *_vidMem;
- bool _ownVidMem;
+ uint16 getCharCount() const;
+ const byte *getCharData(uint8 c) const;
};
-typedef Common::SharedPtr<SurfaceDesc> SurfaceDescPtr;
-
-
class Video {
public:
-#define GDR_VERSION 4
+#define GDR_VERSION 4
-#define PRIMARY_SURFACE 0x80
-#define RETURN_PRIMARY 0x01
-#define DISABLE_SPR_ALLOC 0x20
-#define SCUMMVM_CURSOR 0x100
+#define PRIMARY_SURFACE 0x80
+#define RETURN_PRIMARY 0x01
+#define DISABLE_SPR_ALLOC 0x20
+#define SCUMMVM_CURSOR 0x100
#include "common/pack-start.h" // START STRUCT PACKING
@@ -132,7 +99,7 @@ public:
int16 _scrollOffsetX;
int16 _scrollOffsetY;
- SurfaceDescPtr _splitSurf;
+ SurfacePtr _splitSurf;
int16 _splitHeight1;
int16 _splitHeight2;
int16 _splitStart;
@@ -140,11 +107,8 @@ public:
int16 _screenDeltaX;
int16 _screenDeltaY;
- Graphics::PaletteLUT *_palLUT;
-
- void freeDriver();
void initPrimary(int16 mode);
- SurfaceDescPtr initSurfDesc(int16 vidMode, int16 width,
+ SurfacePtr initSurfDesc(int16 vidMode, int16 width,
int16 height, int16 flags);
void setSize(bool defaultTo1XScaler);
@@ -154,25 +118,9 @@ public:
void waitRetrace(bool mouse = true);
void sparseRetrace(int max);
- void putPixel(int16 x, int16 y, int16 color, SurfaceDesc &dest);
- virtual void fillRect(SurfaceDesc &dest, int16 left, int16 top,
- int16 right, int16 bottom, int16 color);
- void drawLine(SurfaceDesc &dest, int16 x0, int16 y0, int16 x1, int16 y1,
- int16 color);
- void drawCircle(SurfaceDesc &dest, int16 x0, int16 y0,
- int16 radius, int16 color);
- void clearSurf(SurfaceDesc &dest);
- void drawSprite(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom,
- int16 x, int16 y, int16 transp);
- void drawSpriteDouble(SurfaceDesc &source, SurfaceDesc &dest,
- int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp);
- void drawLetter(int16 item, int16 x, int16 y, const Font &font,
- int16 color1, int16 color2, int16 transp, SurfaceDesc &dest);
void drawPackedSprite(byte *sprBuf, int16 width, int16 height,
- int16 x, int16 y, int16 transp, SurfaceDesc &dest);
- void drawPackedSprite(const char *path, SurfaceDesc &dest,
- int width = 320);
+ int16 x, int16 y, int16 transp, Surface &dest);
+ void drawPackedSprite(const char *path, Surface &dest, int width = 320);
void setPalColor(byte *pal, byte red, byte green, byte blue) {
pal[0] = red << 2;
@@ -196,18 +144,12 @@ public:
virtual char spriteUncompressor(byte *sprBuf, int16 srcWidth,
int16 srcHeight, int16 x, int16 y, int16 transp,
- SurfaceDesc &destDesc) = 0;
-
- virtual void init() {}
-
- virtual void setPrePalette() { }
+ Surface &destDesc) = 0;
Video(class GobEngine *vm);
virtual ~Video();
protected:
- class VideoDriver *_videoDriver;
-
bool _dirtyAll;
Common::List<Common::Rect> _dirtyRects;
@@ -216,16 +158,13 @@ protected:
GobEngine *_vm;
- char initDriver(int16 vidMode);
-
- void initOSD();
- void drawOSDText(const char *text);
+ void drawPacked(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Surface &dest);
};
class Video_v1 : public Video {
public:
virtual char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc);
+ int16 x, int16 y, int16 transp, Surface &destDesc);
Video_v1(GobEngine *vm);
virtual ~Video_v1() {}
@@ -234,7 +173,7 @@ public:
class Video_v2 : public Video_v1 {
public:
virtual char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc);
+ int16 x, int16 y, int16 transp, Surface &destDesc);
Video_v2(GobEngine *vm);
virtual ~Video_v2() {}
@@ -243,14 +182,7 @@ public:
class Video_v6 : public Video_v2 {
public:
virtual char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc);
-
- virtual void fillRect(SurfaceDesc &dest, int16 left, int16 top,
- int16 right, int16 bottom, int16 color);
-
- virtual void init();
-
- virtual void setPrePalette();
+ int16 x, int16 y, int16 transp, Surface &destDesc);
Video_v6(GobEngine *vm);
virtual ~Video_v6() {}
@@ -260,30 +192,17 @@ private:
void buildPalLUT();
- void shadeRect(SurfaceDesc &dest,
+ void shadeRect(Surface &dest,
int16 left, int16 top, int16 right, int16 bottom, byte color, byte strength);
- void drawPacked(const byte *sprBuf, int16 x, int16 y, SurfaceDesc &surfDesc);
- void drawYUVData(const byte *srcData, SurfaceDesc &destDesc,
+ void drawPacked(const byte *sprBuf, int16 x, int16 y, Surface &surfDesc);
+ void drawYUVData(const byte *srcData, Surface &destDesc,
int16 width, int16 height, int16 x, int16 y);
- void drawYUV(SurfaceDesc &destDesc, int16 x, int16 y,
+ void drawYUV(Surface &destDesc, int16 x, int16 y,
int16 dataWidth, int16 dataHeight, int16 width, int16 height,
const byte *dataY, const byte *dataU, const byte *dataV);
};
-class VideoDriver {
-public:
- VideoDriver() {}
- virtual ~VideoDriver() {}
- virtual void drawSprite(SurfaceDesc &source, SurfaceDesc &dest, int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) = 0;
- virtual void drawSpriteDouble(SurfaceDesc &source, SurfaceDesc &dest, int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) = 0;
- virtual void fillRect(SurfaceDesc &dest, int16 left, int16 top, int16 right, int16 bottom, byte color) = 0;
- virtual void putPixel(int16 x, int16 y, byte color, SurfaceDesc &dest) = 0;
- virtual void drawLetter(unsigned char item, int16 x, int16 y, const Font &font, byte color1, byte color2, byte transp, SurfaceDesc &dest) = 0;
- virtual void drawLine(SurfaceDesc &dest, int16 x0, int16 y0, int16 x1, int16 y1, byte color) = 0;
- virtual void drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, SurfaceDesc &dest) = 0;
-};
-
} // End of namespace Gob
#endif // GOB_VIDEO_H
diff --git a/engines/gob/video_v1.cpp b/engines/gob/video_v1.cpp
index 699ac5f934..5c2f17d7dd 100644
--- a/engines/gob/video_v1.cpp
+++ b/engines/gob/video_v1.cpp
@@ -34,9 +34,9 @@ Video_v1::Video_v1(GobEngine *vm) : Video(vm) {
}
char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc) {
+ int16 x, int16 y, int16 transp, Surface &destDesc) {
byte *memBuffer;
- byte *srcPtr, *destPtr, *linePtr;
+ byte *srcPtr;
byte temp;
uint16 sourceLeft;
uint16 cmdVar;
@@ -46,7 +46,7 @@ char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
int16 bufPos;
int16 strLen;
- _vm->validateVideoMode(destDesc._vidMode);
+ //_vm->validateVideoMode(destDesc._vidMode);
if (sprBuf[0] != 1)
return 0;
@@ -55,9 +55,8 @@ char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
return 0;
if (sprBuf[2] == 2) {
- SurfaceDesc sourceDesc(0x13, srcWidth, srcHeight, sprBuf + 3);
- Video::drawSprite(sourceDesc, destDesc, 0, 0, srcWidth - 1,
- srcHeight - 1, x, y, transp);
+ Surface sourceDesc(srcWidth, srcHeight, 1, sprBuf + 3);
+ destDesc.blit(sourceDesc, 0, 0, srcWidth - 1, srcHeight - 1, x, y, (transp == 0) ? -1 : 0);
return 1;
} else {
memBuffer = new byte[4114];
@@ -66,12 +65,12 @@ char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
srcPtr = sprBuf + 3;
sourceLeft = READ_LE_UINT16(srcPtr);
- destPtr = destDesc.getVidMem() + destDesc.getWidth() * y + x;
+ Pixel destPtr = destDesc.get(x, y);
curWidth = 0;
curHeight = 0;
- linePtr = destPtr;
+ Pixel linePtr = destPtr;
srcPtr += 4;
for (offset = 0; offset < 4078; offset++)
@@ -88,7 +87,7 @@ char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
if ((cmdVar & 1) != 0) {
temp = *srcPtr++;
if ((temp != 0) || (transp == 0))
- *destPtr = temp;
+ destPtr.set(temp);
destPtr++;
curWidth++;
if (curWidth >= srcWidth) {
@@ -116,7 +115,7 @@ char Video_v1::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
for (counter2 = 0; counter2 < strLen; counter2++) {
temp = memBuffer[(offset + counter2) % 4096];
if ((temp != 0) || (transp == 0))
- *destPtr = temp;
+ destPtr.set(temp);
destPtr++;
curWidth++;
diff --git a/engines/gob/video_v2.cpp b/engines/gob/video_v2.cpp
index 98cf4a5d4f..c908ccf7b1 100644
--- a/engines/gob/video_v2.cpp
+++ b/engines/gob/video_v2.cpp
@@ -34,9 +34,9 @@ Video_v2::Video_v2(GobEngine *vm) : Video_v1(vm) {
}
char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc) {
+ int16 x, int16 y, int16 transp, Surface &destDesc) {
byte *memBuffer;
- byte *srcPtr, *destPtr, *linePtr;
+ byte *srcPtr;
byte temp;
uint32 sourceLeft;
uint16 cmdVar;
@@ -47,7 +47,7 @@ char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
int16 strLen;
int16 lenCmd;
- _vm->validateVideoMode(destDesc._vidMode);
+ //_vm->validateVideoMode(destDesc._vidMode);
if (sprBuf[0] != 1)
return 0;
@@ -56,9 +56,8 @@ char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
return 0;
if (sprBuf[2] == 2) {
- SurfaceDesc sourceDesc(0x13, srcWidth, srcHeight, sprBuf + 3);
- Video::drawSprite(sourceDesc, destDesc, 0, 0, srcWidth - 1,
- srcHeight - 1, x, y, transp);
+ Surface sourceDesc(srcWidth, srcHeight, 1, sprBuf + 3);
+ destDesc.blit(sourceDesc, 0, 0, srcWidth - 1, srcHeight - 1, x, y, (transp == 0) ? -1 : 0);
return 1;
} else if (sprBuf[2] == 1) {
memBuffer = new byte[4370];
@@ -70,12 +69,12 @@ char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
sourceLeft = READ_LE_UINT32(srcPtr);
- destPtr = destDesc.getVidMem() + destDesc.getWidth() * y + x;
+ Pixel destPtr = destDesc.get(x, y);
curWidth = 0;
curHeight = 0;
- linePtr = destPtr;
+ Pixel linePtr = destPtr;
srcPtr += 4;
if ((READ_LE_UINT16(srcPtr) == 0x1234) && (READ_LE_UINT16(srcPtr + 2) == 0x5678)) {
@@ -99,7 +98,7 @@ char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
temp = *srcPtr++;
if ((temp != 0) || (transp == 0))
- *destPtr = temp;
+ destPtr.set(temp);
destPtr++;
curWidth++;
@@ -133,7 +132,7 @@ char Video_v2::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
temp = memBuffer[(offset + counter2) % 4096];
if ((temp != 0) || (transp == 0))
- *destPtr = temp;
+ destPtr.set(temp);
destPtr++;
curWidth++;
diff --git a/engines/gob/video_v6.cpp b/engines/gob/video_v6.cpp
index 7285cd8958..7bf728034b 100644
--- a/engines/gob/video_v6.cpp
+++ b/engines/gob/video_v6.cpp
@@ -25,7 +25,8 @@
#include "common/endian.h"
#include "common/savefile.h"
-#include "graphics/dither.h"
+
+#include "graphics/conversion.h"
#include "gob/gob.h"
#include "gob/video.h"
@@ -38,46 +39,8 @@ namespace Gob {
Video_v6::Video_v6(GobEngine *vm) : Video_v2(vm) {
}
-void Video_v6::setPrePalette() {
- byte *tpal = (byte *)_vm->_draw->_vgaPalette;
- const byte *fpal = (const byte *)_ditherPalette;
-
- for (int i = 0; i < 256; i++) {
- byte r, g, b;
-
- Graphics::PaletteLUT::YUV2RGB(fpal[i * 3 + 0], fpal[i * 3 + 1], fpal[i * 3 + 2],
- r, g, b);
-
- tpal[i * 3 + 0] = r >> 2;
- tpal[i * 3 + 1] = g >> 2;
- tpal[i * 3 + 2] = b >> 2;
- }
- _vm->_global->_pPaletteDesc->vgaPal = _vm->_draw->_vgaPalette;
- _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
-
-}
-
-void Video_v6::init() {
- initOSD();
-
- buildPalLUT();
-}
-
-void Video_v6::buildPalLUT() {
- char text[30];
-
- _palLUT->setPalette(_ditherPalette, Graphics::PaletteLUT::kPaletteYUV, 8, 0);
-
- sprintf(text, "Building palette table");
- drawOSDText(text);
-
- for (int i = 0; (i < 32) && !_vm->shouldQuit(); i++)
- _palLUT->buildNext();
-}
-
char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
- int16 x, int16 y, int16 transp, SurfaceDesc &destDesc) {
- _vm->validateVideoMode(destDesc._vidMode);
+ int16 x, int16 y, int16 transp, Surface &destDesc) {
if ((sprBuf[0] == 1) && (sprBuf[1] == 3)) {
drawPacked(sprBuf, x, y, destDesc);
@@ -93,9 +56,7 @@ char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
if (Video_v2::spriteUncompressor(sprBuf, srcWidth, srcHeight, x, y, transp, destDesc))
return 1;
- _vm->validateVideoMode(destDesc._vidMode);
-
- _videoDriver->drawPackedSprite(sprBuf, srcWidth, srcHeight, x, y, transp, destDesc);
+ Video::drawPacked(sprBuf, srcWidth, srcHeight, x, y, transp, destDesc);
return 1;
}
@@ -104,7 +65,8 @@ char Video_v6::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
return 1;
}
-void Video_v6::fillRect(SurfaceDesc &dest,
+/*
+void Video_v6::fillRect(Surface &dest,
int16 left, int16 top, int16 right, int16 bottom, int16 color) {
if (!(color & 0xFF00)) {
@@ -136,17 +98,21 @@ void Video_v6::fillRect(SurfaceDesc &dest,
byte strength = 16 - (((uint16) color) >> 12);
shadeRect(dest, left, top, right, bottom, color, strength);
}
+*/
-void Video_v6::shadeRect(SurfaceDesc &dest,
+void Video_v6::shadeRect(Surface &dest,
int16 left, int16 top, int16 right, int16 bottom, byte color, byte strength) {
+ warning("TODO: Video_v6::shadeRect()");
+
+ /*
int width = right - left + 1;
int height = bottom - top + 1;
int dWidth = dest.getWidth();
byte *vidMem = dest.getVidMem() + dWidth * top + left;
byte sY, sU, sV;
- _palLUT->getEntry(color, sY, sU, sV);
+ //_palLUT->getEntry(color, sY, sU, sV);
int shadeY = sY * (16 - strength);
int shadeU = sU * (16 - strength);
@@ -176,9 +142,10 @@ void Video_v6::shadeRect(SurfaceDesc &dest,
}
delete dither;
+ */
}
-void Video_v6::drawPacked(const byte *sprBuf, int16 x, int16 y, SurfaceDesc &surfDesc) {
+void Video_v6::drawPacked(const byte *sprBuf, int16 x, int16 y, Surface &surfDesc) {
const byte *data = sprBuf + 2;
int16 width = READ_LE_UINT16(data);
@@ -204,7 +171,7 @@ void Video_v6::drawPacked(const byte *sprBuf, int16 x, int16 y, SurfaceDesc &sur
delete[] uncBuf;
}
-void Video_v6::drawYUVData(const byte *srcData, SurfaceDesc &destDesc,
+void Video_v6::drawYUVData(const byte *srcData, Surface &destDesc,
int16 width, int16 height, int16 x, int16 y) {
int16 dataWidth = width;
@@ -223,106 +190,46 @@ void Video_v6::drawYUVData(const byte *srcData, SurfaceDesc &destDesc,
}
-void Video_v6::drawYUV(SurfaceDesc &destDesc, int16 x, int16 y,
+void Video_v6::drawYUV(Surface &destDesc, int16 x, int16 y,
int16 dataWidth, int16 dataHeight, int16 width, int16 height,
const byte *dataY, const byte *dataU, const byte *dataV) {
- byte *vidMem = destDesc.getVidMem() + y * destDesc.getWidth() + x;
+ const Graphics::PixelFormat &pixelFormat = _vm->getPixelFormat();
if ((x + width - 1) >= destDesc.getWidth())
width = destDesc.getWidth() - x;
if ((y + height - 1) >= destDesc.getHeight())
height = destDesc.getHeight() - y;
- Graphics::SierraLight *dither =
- new Graphics::SierraLight(width, _palLUT);
+ Pixel dst = destDesc.get(x, y);
for (int i = 0; i < height; i++) {
- byte *dest = vidMem;
+ Pixel dstRow = dst;
+
const byte *srcY = dataY + i * dataWidth;
const byte *srcU = dataU + (i >> 2) * (dataWidth >> 2);
const byte *srcV = dataV + (i >> 2) * (dataWidth >> 2);
for (int j = 0; j < (width >> 2); j++, srcU++, srcV++) {
- for (int n = 0; n < 4; n++, dest++, srcY++) {
+ for (int n = 0; n < 4; n++, dstRow++, srcY++) {
byte dY = *srcY << 1, dU = *srcU << 1, dV = *srcV << 1;
- *dest = (dY == 0) ? 0 : dither->dither(dY, dU, dV, j * 4 + n);
+ byte r, g, b;
+ Graphics::YUV2RGB(dY, dU, dV, r, g, b);
+
+ if (dY != 0) {
+ uint32 c = pixelFormat.RGBToColor(r, g, b);
+
+ dstRow.set((c == 0) ? 1 : c);
+ } else
+ dstRow.set(0);
+
}
}
- dither->nextLine();
- vidMem += destDesc.getWidth();
+ dst += destDesc.getWidth();
}
- delete dither;
}
-const byte Video_v6::_ditherPalette[768] = {
- 0x00,0x80,0x80,0x26,0x6B,0xC0,0x4B,0x56,0x4B,0x71,0x41,0x8B,
- 0x0E,0xC0,0x76,0x34,0xAB,0xB6,0x59,0x96,0x41,0xBE,0x81,0x81,
- 0xCF,0x77,0x75,0xC1,0x9B,0x6C,0x7F,0x81,0x81,0x7F,0x81,0x81,
- 0x0A,0x8C,0x8A,0x0A,0xB0,0x79,0x0E,0x9D,0x76,0x0E,0x79,0x76,
- 0x10,0x77,0x75,0x09,0x7B,0x8B,0x16,0x74,0xA6,0x16,0x74,0xA6,
- 0x12,0x91,0x93,0x13,0xB5,0x72,0x1B,0x9E,0x6D,0x1A,0x7A,0x6D,
- 0x1C,0x71,0x6C,0x1B,0x72,0x8C,0x1B,0x71,0xAE,0x1B,0x71,0xAE,
- 0x21,0x93,0x93,0x21,0xB8,0x6F,0x27,0xA3,0x64,0x27,0x7F,0x65,
- 0x29,0x69,0x68,0x28,0x69,0x8E,0x23,0x6F,0xB7,0x2A,0x69,0xB2,
- 0x36,0x94,0x92,0x35,0xB9,0x6E,0x35,0xA6,0x5D,0x34,0x80,0x5D,
- 0x39,0x61,0x6B,0x3A,0x60,0x8F,0x34,0x6E,0xB9,0x39,0x60,0xB5,
- 0x46,0x93,0x95,0x48,0xB6,0x6F,0x45,0xA6,0x5C,0x47,0x80,0x5C,
- 0x4B,0x56,0x6D,0x4B,0x56,0x91,0x47,0x6F,0xB7,0x4A,0x57,0xB5,
- 0x5A,0x94,0x92,0x59,0xB9,0x6E,0x59,0xA6,0x5D,0x59,0x82,0x5D,
- 0x5B,0x4D,0x6D,0x5A,0x4E,0x92,0x5B,0x6F,0xB7,0x5C,0x4D,0xB6,
- 0x6D,0x94,0x93,0x6D,0xB8,0x6F,0x6D,0xA5,0x5D,0x6C,0x7F,0x5E,
- 0x6C,0x4A,0x70,0x6D,0x4A,0x94,0x6C,0x6E,0xB9,0x6C,0x4A,0xBA,
- 0x7E,0x94,0x93,0x80,0xB6,0x6F,0x7D,0xA6,0x5D,0x7F,0x81,0x5B,
- 0x7F,0x4A,0x6F,0x7F,0x4A,0x92,0x7E,0x6F,0xB7,0x7E,0x4B,0xB7,
- 0x92,0x94,0x93,0x90,0xB8,0x6F,0x91,0xA5,0x5C,0x90,0x81,0x5D,
- 0x91,0x4B,0x6D,0x91,0x4C,0x93,0x93,0x6F,0xB7,0x92,0x4B,0xB7,
- 0xA5,0x91,0x93,0xA2,0xB2,0x6F,0xA5,0xA6,0x5D,0xA4,0x80,0x5D,
- 0xA3,0x4A,0x6F,0xA4,0x49,0x93,0xA4,0x6F,0xB9,0xA3,0x4B,0xB9,
- 0xB6,0x94,0x93,0xB4,0xA9,0x71,0xB6,0xA5,0x5C,0xB8,0x80,0x5C,
- 0xB7,0x4B,0x6F,0xB6,0x4C,0x93,0xB5,0x70,0xB3,0xB5,0x4C,0xB3,
- 0xC9,0x93,0x92,0xC3,0xA0,0x72,0xC7,0x9E,0x5E,0xC9,0x82,0x5D,
- 0xCA,0x4B,0x6E,0xCA,0x4B,0x93,0xC2,0x73,0xA9,0xC2,0x4F,0xA9,
- 0xDD,0x92,0x93,0xD4,0x97,0x74,0xD9,0x94,0x60,0xDD,0x80,0x5E,
- 0xDB,0x4B,0x6F,0xDD,0x4A,0x93,0xCE,0x76,0xA1,0xCE,0x52,0xA1,
- 0xE8,0x8B,0x8E,0xE6,0x8C,0x76,0xE7,0x8C,0x61,0xE6,0x86,0x62,
- 0xE3,0x51,0x78,0xEA,0x4D,0x8D,0xDC,0x79,0x97,0xDC,0x55,0x97,
- 0xFA,0x81,0x81,0xF8,0x82,0x83,0xF4,0x85,0x77,0xF2,0x79,0x79,
- 0xF4,0x65,0x86,0xF8,0x75,0x83,0xEF,0x87,0x89,0xF0,0x67,0x89,
- 0xEE,0x81,0x81,0xE8,0x8B,0x85,0xED,0x88,0x71,0xEA,0x71,0x73,
- 0xEE,0x5D,0x81,0xEA,0x71,0x8D,0xE4,0x87,0x91,0xE4,0x63,0x91,
- 0xDB,0x81,0x81,0xD7,0x95,0x83,0xDB,0x93,0x6F,0xDB,0x6F,0x6F,
- 0xDC,0x5C,0x80,0xDD,0x6E,0x93,0xD7,0x83,0x9B,0xD6,0x5F,0x9B,
- 0xCA,0x81,0x81,0xC8,0x9D,0x82,0xC8,0x94,0x6E,0xCA,0x6F,0x6E,
- 0xC9,0x5D,0x81,0xCA,0x6F,0x93,0xC9,0x82,0xA5,0xC8,0x5E,0xA5,
- 0xB6,0x81,0x81,0xB6,0xA5,0x80,0xB7,0x93,0x6F,0xB7,0x6F,0x6F,
- 0xB6,0x5D,0x81,0xB6,0x70,0x93,0xB8,0x80,0xA5,0xB7,0x5C,0xA5,
- 0xA3,0x81,0x81,0xA4,0xA7,0x81,0xA3,0x92,0x6F,0xA3,0x6E,0x6F,
- 0xA5,0x5C,0x7F,0xA5,0x6D,0x93,0xA4,0x80,0xA5,0xA4,0x5C,0xA5,
- 0x92,0x81,0x81,0x93,0xA5,0x81,0x90,0x94,0x6F,0x92,0x6F,0x6D,
- 0x92,0x5D,0x81,0x92,0x70,0x93,0x91,0x82,0xA7,0x91,0x5E,0xA7,
- 0x7F,0x81,0x81,0x7F,0xA5,0x81,0x80,0x92,0x70,0x7F,0x6E,0x6F,
- 0x7F,0x5D,0x81,0x7D,0x70,0x93,0x80,0x80,0xA5,0x80,0x5C,0xA5,
- 0x6B,0x81,0x81,0x6C,0xA7,0x80,0x6D,0x94,0x6F,0x6C,0x6E,0x6F,
- 0x6D,0x5B,0x80,0x6D,0x6E,0x93,0x6C,0x81,0xA5,0x6C,0x5D,0xA5,
- 0x5A,0x81,0x81,0x5A,0xA5,0x81,0x59,0x95,0x6E,0x5A,0x6F,0x6E,
- 0x5B,0x5D,0x81,0x59,0x70,0x93,0x5A,0x81,0xA7,0x5A,0x5D,0xA7,
- 0x47,0x81,0x81,0x47,0xA5,0x80,0x47,0x92,0x6F,0x47,0x6E,0x6F,
- 0x46,0x5D,0x81,0x46,0x6F,0x95,0x49,0x81,0xA5,0x48,0x5D,0xA5,
- 0x32,0x81,0x81,0x33,0xA7,0x81,0x35,0x95,0x6E,0x34,0x6F,0x6F,
- 0x36,0x62,0x7E,0x35,0x6E,0x93,0x34,0x80,0xA5,0x36,0x62,0xA4,
- 0x23,0x81,0x81,0x23,0xA5,0x80,0x20,0x94,0x6F,0x22,0x6F,0x6D,
- 0x26,0x6B,0x7E,0x21,0x6F,0x93,0x22,0x82,0xA7,0x25,0x6C,0xA4,
- 0x0E,0x81,0x81,0x0F,0xA5,0x81,0x13,0x91,0x73,0x15,0x75,0x71,
- 0x14,0x75,0x7D,0x11,0x77,0x93,0x15,0x7D,0xA1,0x16,0x74,0xA1,
- 0x00,0x80,0x80,0x07,0xA0,0x7B,0x05,0x8F,0x7D,0x09,0x7B,0x7A,
- 0x07,0x7D,0x7B,0x07,0x7C,0x8C,0x0E,0x78,0x98,0x0E,0x78,0x98,
- 0x7F,0x81,0x81,0x80,0x83,0x81,0xF7,0x7C,0x84,0x9E,0x83,0x80,
- 0x7F,0x81,0x81,0x4B,0x56,0xFE,0x93,0x2D,0x17,0xDE,0x03,0x95,
- 0x1C,0xFE,0x6C,0x67,0xD4,0xEA,0xAF,0xAB,0x03,0xFA,0x81,0x81
-};
-
} // End of namespace Gob
diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp
index 9e49bfc092..f349413b93 100644
--- a/engines/gob/videoplayer.cpp
+++ b/engines/gob/videoplayer.cpp
@@ -155,8 +155,8 @@ int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties
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);
+ video->decoder->setSurfaceMemory(video->surface->getData(),
+ video->surface->getWidth(), video->surface->getHeight(), video->surface->getBPP());
if (!ownSurf || (ownSurf && screenSize)) {
if ((properties.x >= 0) || (properties.y >= 0))
diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h
index d91d0a3845..c154254455 100644
--- a/engines/gob/videoplayer.h
+++ b/engines/gob/videoplayer.h
@@ -137,7 +137,7 @@ private:
Graphics::CoktelDecoder *decoder;
Common::String fileName;
- SurfaceDescPtr surface;
+ SurfacePtr surface;
Video();
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index b30c2361d2..a6a92eb521 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -54,7 +54,8 @@ static const GroovieGameDescription gameDescriptions[] = {
{
"t7g", "",
AD_ENTRY1s("script.grv", "d1b8033b40aa67c076039881eccce90d", 16659),
- Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, Common::GUIO_NONE
+ Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM
},
kGroovieT7G, 0
},
@@ -64,7 +65,8 @@ static const GroovieGameDescription gameDescriptions[] = {
{
"t7g", "",
AD_ENTRY1s("T7GMac", "a139540fa2be2247005ccf888b7231e3", 1830783),
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, Common::GUIO_NONE
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM
},
kGroovieT7G, 0
},
@@ -78,7 +80,8 @@ static const GroovieGameDescription gameDescriptions[] = {
{ "intro.gjd", 0, NULL, 31711554},
{ NULL, 0, NULL, 0}
},
- Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS, Common::GUIO_NONE
+ Common::RU_RUS, Common::kPlatformPC, ADGF_NO_FLAGS,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM
},
kGroovieT7G, 0
},
@@ -89,7 +92,8 @@ static const GroovieGameDescription gameDescriptions[] = {
{
"11h", "",
AD_ENTRY1s("disk.1", "5c0428cd3659fc7bbcd0aa16485ed5da", 227),
- Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, Common::GUIO_NONE
+ Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM
},
kGroovieV2, 1
},
@@ -99,7 +103,8 @@ static const GroovieGameDescription gameDescriptions[] = {
{
"11h", "Demo",
AD_ENTRY1s("disk.1", "aacb32ce07e0df2894bd83a3dee40c12", 70),
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, Common::GUIO_NOLAUNCHLOAD
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, Common::GUIO_NOLAUNCHLOAD |
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM
},
kGroovieV2, 1
},
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 82eda1efe2..c7c3ef85f8 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -321,13 +321,18 @@ void GroovieEngine::errorString(const char *buf_input, char *buf_output, int buf
}
void GroovieEngine::syncSoundSettings() {
- _musicPlayer->setUserVolume(ConfMan.getInt("music_volume"));
- // VDX videos just contain one digital audio track, which can be used for
+ bool mute = ConfMan.getBool("mute");
+
+ // Set the music volume
+ _musicPlayer->setUserVolume(mute ? 0 : ConfMan.getInt("music_volume"));
+
+ // Videos just contain one digital audio track, which can be used for
// both SFX or Speech, but the engine doesn't know what they contain, so
// we have to use just one volume setting for videos.
// We use "speech" because most users will want to change the videos
// volume when they can't hear the speech because of the music.
- _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("speech_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType,
+ mute ? 0 : ConfMan.getInt("speech_volume"));
}
bool GroovieEngine::canLoadGameStateCurrently() {
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index 34b180a68e..2ad11ee0e6 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -426,10 +426,14 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
setTimbreAD(9, _timbres[i]);
}
} else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
+ _driver->sendMT32Reset();
+
// MT-32
_musicType = MT_MT32;
loadTimbres(gtlName + ".mt");
} else {
+ _driver->sendGMReset();
+
// GM
_musicType = 0;
}
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
new file mode 100644
index 0000000000..0064a39434
--- /dev/null
+++ b/engines/hugo/detection.cpp
@@ -0,0 +1,202 @@
+/* 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/advancedDetector.h"
+
+#include "hugo/hugo.h"
+
+namespace Hugo {
+
+struct HugoGameDescription {
+ ADGameDescription desc;
+ GameType gameType;
+};
+
+uint32 HugoEngine::getFeatures() const {
+ return _gameDescription->desc.flags;
+}
+
+static const PlainGameDescriptor hugoGames[] = {
+ // Games
+ {"hugo1", "Hugo 1: Hugo's House of Horrors"},
+ {"hugo2", "Hugo 2: Hugo's Mystery Adventure"},
+ {"hugo3", "Hugo 3: Hugo's Amazon Adventure"},
+ {0, 0}
+};
+
+static const HugoGameDescription gameDescriptions[] = {
+
+ // Hugo1 DOS
+ {
+ {
+ "hugo1", 0,
+ AD_ENTRY1s("house.art", "c9403b2fe539185c9fd569b6cc4ff5ca", 14811),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo1
+ },
+ // Hugo1 Windows
+ {
+ {
+ "hugo1", 0,
+ AD_ENTRY1s("objects.dat", "3ba0f108f7690a05a34c56a02fbe644a", 126488),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ GF_PACKED,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo1
+ },
+ // Hugo2 DOS
+ {
+ {
+ "hugo2", 0,
+ AD_ENTRY1s("objects.dat", "88a718cc0ff2b3b25d49aaaa69d6d52c", 155240),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ GF_PACKED,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo2
+ },
+ // Hugo2 Windows
+ {
+ {
+ "hugo2", 0,
+ AD_ENTRY1s("objects.dat", "5df4ffc851e66a544c0e95e4e084a806", 158480),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ GF_PACKED,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo2
+ },
+ // Hugo3 DOS
+ {
+ {
+ "hugo3", 0,
+ AD_ENTRY1s("objects.dat", "bb1b061538a445f2eb99b682c0f506cc", 136419),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ GF_PACKED,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo3
+ },
+ // Hugo3 Windows
+ {
+ {
+ "hugo3", 0,
+ AD_ENTRY1s("objects.dat", "c9a8af7aa14cc907434eecee3ddd06d3", 136638),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ GF_PACKED,
+ Common::GUIO_NONE
+ },
+ kGameTypeHugo3
+ },
+
+ {AD_TABLE_END_MARKER, kGameTypeNone}
+};
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)gameDescriptions,
+ // Size of that superset structure
+ sizeof(HugoGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ hugoGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ 0,
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
+};
+
+class HugoMetaEngine : public AdvancedMetaEngine {
+public:
+ HugoMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ const char *getName() const {
+ return "Hugo Engine";
+ }
+
+ const char *getOriginalCopyright() const {
+ return "Hugo Engine (C) 1989-1997 David P. Gray";
+ }
+
+ bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+
+ bool hasFeature(MetaEngineFeature f) const;
+};
+
+bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+ if (gd) {
+ *engine = new HugoEngine(syst, (const HugoGameDescription *)gd);
+ ((HugoEngine *)*engine)->initGame((const HugoGameDescription *)gd);
+ }
+ return gd != 0;
+}
+
+bool HugoMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return false;
+}
+
+} // End of namespace Hugo
+
+#if PLUGIN_ENABLED_DYNAMIC(HUGO)
+REGISTER_PLUGIN_DYNAMIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine);
+#endif
+
+namespace Hugo {
+
+void HugoEngine::initGame(const HugoGameDescription *gd) {
+ _gameType = gd->gameType;
+ _platform = gd->desc.platform;
+ _packedFl = (getFeatures() & GF_PACKED);
+ _gameVariant = _gameType - 1 + ((_platform == Common::kPlatformWindows) ? 0 : 3);
+
+ // Generate filenames
+ _initFilename = _targetName + "-00.SAV";
+ _saveFilename = _targetName + "-%d.SAV";
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp
new file mode 100644
index 0000000000..3a8d0d4e89
--- /dev/null
+++ b/engines/hugo/display.cpp
@@ -0,0 +1,444 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// Display.c - DIB related code for HUGOWIN
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+#define NUM_COLORS 16 // Num colors to save in palette
+#define DMAX 16 // Size of add/restore rect lists
+#define BMAX (DMAX * 2) // Size of dirty rect blit list
+
+#define INX(X, B) (X >= B->x && X <= B->x + B->dx)
+#define INY(Y, B) (Y >= B->y && Y <= B->y + B->dy)
+#define OVERLAP(A, B) ((INX(A->x, B) || INX(A->x + A->dx, B) || INX(B->x, A) || INX(B->x + B->dx, A)) && (INY(A->y, B) || INY(A->y + A->dy, B) || INY(B->y, A) || INY(B->y + B->dy, A)))
+
+Screen::Screen(HugoEngine &vm) : _vm(vm) {
+
+}
+
+Screen::~Screen() {
+}
+
+void Screen::createPal() {
+ debugC(1, kDebugDisplay, "createPal");
+
+ g_system->setPalette(_vm._palette, 0, NUM_COLORS);
+}
+
+void Screen::initDisplay() {
+ debugC(1, kDebugDisplay, "initDisplay");
+ // Create logical palette
+ createPal();
+}
+
+// Move an image from source to destination
+void Screen::moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2) {
+ debugC(3, kDebugDisplay, "moveImage(srcImage, %d, %d, %d, %d, %d, dstImage, %d, %d, %d)", x1, y1, dx, dy, width1, x2, y2, width2);
+
+ int16 wrap_src = width1 - dx; // Wrap to next src row
+ int16 wrap_dst = width2 - dx; // Wrap to next dst row
+
+ srcImage += y1 * width1 + x1; // Offset into src image
+ dstImage += y2 * width2 + x2; // offset into dst image
+
+ while (dy--) { // For each row
+ for (int16 x = dx; x--;) // For each column
+ *dstImage++ = *srcImage++;
+ srcImage += wrap_src; // Wrap to next line
+ dstImage += wrap_dst;
+ }
+}
+
+void Screen::displayBackground() {
+ debugC(1, kDebugDisplay, "displayBackground");
+
+ g_system->copyRectToScreen(_frontBuffer, 320, 0, 0, 320, 200);
+}
+
+// Blit the supplied rectangle from _frontBuffer to the screen
+void Screen::displayRect(int16 x, int16 y, int16 dx, int16 dy) {
+ debugC(3, kDebugDisplay, "displayRect(%d, %d, %d, %d)", x, y, dx, dy);
+
+ g_system->copyRectToScreen(&_frontBuffer[x + y * 320], 320, x, y, dx, dy);
+}
+
+void Screen::remapPal(uint16 oldIndex, uint16 newIndex) {
+// Change a color by remapping supplied palette index with new index
+ debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex);
+
+ warning("STUB: Remap_pal()");
+ //bminfo.bmiColors[oldIndex] = ctab[newIndex];
+}
+
+void Screen::savePal(Common::WriteStream *f) {
+ debugC(1, kDebugDisplay, "savePal");
+
+ warning("STUB: savePal()");
+ //fwrite(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+}
+
+void Screen::restorePal(Common::SeekableReadStream *f) {
+ debugC(1, kDebugDisplay, "restorePal");
+
+ warning("STUB: restorePal()");
+ //fread(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+}
+
+
+// Set the new background color
+void Screen::setBackgroundColor(long color) {
+ debugC(1, kDebugDisplay, "setBackgroundColor(%ld)", color);
+
+ // How??? Translate existing pixels in dib before objects rendered?
+}
+
+// Return the overlay state (Foreground/Background) of the currently
+// processed object by looking down the current column for an overlay
+// base bit set (in which case the object is foreground).
+overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
+ debugC(4, kDebugDisplay, "findOvl");
+
+ for (; y < seq_p->lines; y++) { // Each line in object
+ image_pt ovb_p = _vm.getBaseBoundaryOverlay() + ((uint16)(dst_p - _frontBuffer) >> 3); // Ptr into overlay bits
+ if (*ovb_p & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set
+ return FG; // Found a bit - must be foreground
+ dst_p += XPIX;
+ }
+
+ return BG; // No bits set, must be background
+}
+
+// Merge an object frame into _frontBuffer at sx, sy and update rectangle list.
+// If fore TRUE, force object above any overlay
+void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) {
+ debugC(3, kDebugDisplay, "displayFrame(%d, %d, seq, %d)", sx, sy, (foreFl) ? 1 : 0);
+
+ image_pt image = seq->imagePtr; // Ptr to object image data
+ image_pt subFrontBuffer = &_frontBuffer[sy * XPIX + sx]; // Ptr to offset in _frontBuffer
+ image_pt overlay = &_vm.getFirstOverlay()[(sy * XPIX + sx) >> 3]; // Ptr to overlay data
+ int16 frontBufferwrap = XPIX - seq->x2 - 1; // Wraps dest_p after each line
+ int16 imageWrap = seq->bytesPerLine8 - seq->x2 - 1;
+
+ overlayState_t overlayState = UNDEF; // Overlay state of object
+ for (uint16 y = 0; y < seq->lines; y++) { // Each line in object
+ for (uint16 x = 0; x <= seq->x2; x++) {
+ if (*image) { // Non-transparent
+ overlay = _vm.getFirstOverlay() + ((uint16)(subFrontBuffer - _frontBuffer) >> 3); // Ptr into overlay bits
+ if (*overlay & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) { // Overlay bit is set
+ if (overlayState == UNDEF) // Overlay defined yet?
+ overlayState = findOvl(seq, subFrontBuffer, y);// No, find it.
+ if (foreFl || overlayState == FG) // Object foreground
+ *subFrontBuffer = *image; // Copy pixel
+ } else { // No overlay
+ *subFrontBuffer = *image; // Copy pixel
+ }
+ }
+ image++;
+ subFrontBuffer++;
+ }
+ image += imageWrap;
+ subFrontBuffer += frontBufferwrap;
+ }
+
+ // Add this rectangle to the display list
+ displayList(D_ADD, sx, sy, seq->x2 + 1, seq->lines);
+}
+
+// Merge rectangles A,B leaving result in B
+void Screen::merge(rect_t *rectA, rect_t *rectB) {
+ debugC(6, kDebugDisplay, "merge");
+
+ int16 xa = rectA->x + rectA->dx; // Find x2,y2 for each rectangle
+ int16 xb = rectB->x + rectB->dx;
+ int16 ya = rectA->y + rectA->dy;
+ int16 yb = rectB->y + rectB->dy;
+
+ rectB->x = MIN(rectA->x, rectB->x); // Minimum x,y
+ rectB->y = MIN(rectA->y, rectB->y);
+ rectB->dx = MAX(xa, xb) - rectB->x; // Maximum dx,dy
+ rectB->dy = MAX(ya, yb) - rectB->y;
+}
+
+// Coalesce the rectangles in the restore/add list into one unified
+// blist. len is the sizes of alist or rlist. blen is current length
+// of blist. bmax is the max size of the blist. Note that blist can
+// have holes, in which case dx = 0. Returns used length of blist.
+int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax) {
+ debugC(4, kDebugDisplay, "mergeLists");
+
+ int16 coalesce[BMAX]; // List of overlapping rects
+ // Process the list
+ for (int16 a = 0; a < len; a++, list++) {
+ // Compile list of overlapping rectangles in blit list
+ int16 c = 0;
+ rect_t *bp = blist;
+ for (int16 b = 0; b < blen; b++, bp++) {
+ if (bp->dx) // blist entry used
+ if (OVERLAP(list, bp))
+ coalesce[c++] = b;
+ }
+
+ // Any overlapping blit rects?
+ if (c == 0) { // None, add a new entry
+ blist[blen++] = *list;
+ } else { // At least one overlapping
+ // Merge add-list entry with first blist entry
+ bp = &blist[coalesce[0]];
+ merge(list, bp);
+
+ // Merge any more blist entries
+ while (--c) {
+ rect_t *cp = &blist[coalesce[c]];
+ merge(cp, bp);
+ cp->dx = 0; // Delete entry
+ }
+ }
+ }
+ return blen;
+}
+
+// Process the display list
+// Trailing args are int16 x,y,dx,dy for the D_ADD operation
+void Screen::displayList(dupdate_t update, ...) {
+ debugC(6, kDebugDisplay, "displayList");
+
+ static int16 addIndex, restoreIndex; // Index into add/restore lists
+ static rect_t restoreList[DMAX]; // The restore list
+ static rect_t addList[DMAX]; // The add list
+ static rect_t blistList[BMAX]; // The blit list
+ int16 blitLength = 0; // Length of blit list
+ va_list marker; // Args used for D_ADD operation
+ rect_t *p; // Ptr to dlist entry
+
+ switch (update) {
+ case D_INIT: // Init lists, restore whole screen
+ addIndex = restoreIndex = 0;
+ memcpy(_frontBuffer, _backBuffer, sizeof(_frontBuffer));
+ break;
+ case D_ADD: // Add a rectangle to list
+ if (addIndex >= DMAX) {
+ Utils::Warn("%s", "Display list exceeded");
+ return;
+ }
+ va_start(marker, update); // Initialize variable arguments
+ p = &addList[addIndex];
+ p->x = va_arg(marker, int); // x
+ p->y = va_arg(marker, int); // y
+ p->dx = va_arg(marker, int); // dx
+ p->dy = va_arg(marker, int); // dy
+ va_end(marker); // Reset variable arguments
+ addIndex++;
+ break;
+ case D_DISPLAY: // Display whole list
+ // Don't blit if newscreen just loaded because _frontBuffer will
+ // get blitted via InvalidateRect() at end of this cycle
+ // and blitting here causes objects to appear too soon.
+ if (_vm.getGameStatus().newScreenFl) {
+ _vm.getGameStatus().newScreenFl = false;
+ break;
+ }
+
+ // Coalesce restore-list, add-list into combined blit-list
+ blitLength = mergeLists(restoreList, blistList, restoreIndex, blitLength, BMAX);
+ blitLength = mergeLists(addList, blistList, addIndex, blitLength, BMAX);
+
+ // Blit the combined blit-list
+ for (restoreIndex = 0, p = blistList; restoreIndex < blitLength; restoreIndex++, p++) {
+ if (p->dx) // Marks a used entry
+ displayRect(p->x, p->y, p->dx, p->dy);
+ }
+ break;
+ case D_RESTORE: // Restore each rectangle
+ for (restoreIndex = 0, p = addList; restoreIndex < addIndex; restoreIndex++, p++) {
+ // Restoring from _backBuffer to _frontBuffer
+ restoreList[restoreIndex] = *p; // Copy add-list to restore-list
+ moveImage(_backBuffer, p->x, p->y, p->dx, p->dy, XPIX, _frontBuffer, p->x, p->y, XPIX);
+ }
+ addIndex = 0; // Reset add-list
+ break;
+ }
+}
+
+void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) {
+// Write supplied character (font data) at sx,sy in supplied color
+// Font data as follows:
+//
+// *(fontdata+1) = Font Height (pixels)
+// *(fontdata+1) = Font Width (pixels)
+// *(fontdata+x) = Font Bitmap (monochrome)
+ debugC(2, kDebugDisplay, "writeChr(%d, %d, %d, %d)", sx, sy, color, local_fontdata[0]);
+
+ byte height = local_fontdata[0];
+ byte width = 8; //local_fontdata[1];
+
+ // This can probably be optimized quite a bit...
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int pixel = y * width + x;
+ int bitpos = pixel % 8;
+ int offset = pixel / 8;
+ byte bitTest = (1 << bitpos);
+ if ((local_fontdata[2 + offset] & bitTest) == bitTest)
+ _frontBuffer[(sy + y) * 320 + sx + x] = color;
+ }
+ }
+}
+
+// Returns height of characters in current font
+int16 Screen::fontHeight() {
+ debugC(2, kDebugDisplay, "fontHeight");
+
+ static int16 height[NUM_FONTS] = {5, 7, 8};
+ return height[_fnt - FIRST_FONT];
+}
+
+
+// Returns length of supplied string in pixels
+int16 Screen::stringLength(const char *s) {
+ debugC(2, kDebugDisplay, "stringLength(%s)", s);
+
+ byte **fontArr = _font[_fnt];
+ int16 sum = 0;
+ for (; *s; s++)
+ sum += *(fontArr[(uint)*s] + 1) + 1;
+
+ return sum;
+}
+
+// Return x which would center supplied string
+int16 Screen::center(const char *s) {
+ debugC(1, kDebugDisplay, "center(%s)", s);
+
+ return (int16)((XPIX - stringLength(s)) >> 1);
+}
+
+// Write string at sx,sy in supplied color in current font
+// If sx == CENTER, center it
+void Screen::writeStr(int16 sx, int16 sy, const char *s, byte color) {
+ debugC(2, kDebugDisplay, "writeStr(%d, %d, %s, %d)", sx, sy, s, color);
+
+ if (sx == CENTER)
+ sx = center(s);
+
+ byte **font = _font[_fnt];
+ for (; *s; s++) {
+ writeChr(sx, sy, color, (char *)font[(uint)*s]);
+ sx += *(font[(uint)*s] + 1) + 1;
+ }
+}
+
+// Shadowed version of writestr
+void Screen::shadowStr(int16 sx, int16 sy, const char *s, byte color) {
+ debugC(1, kDebugDisplay, "shadowStr(%d, %d, %s, %d)", sx, sy, s, color);
+
+ if (sx == CENTER)
+ sx = center(s);
+
+ writeStr(sx + 1, sy + 1, s, _TBLACK);
+ writeStr(sx, sy, s, color);
+}
+
+void Screen::userHelp() {
+// Introduce user to the game
+// DOS versions Only
+ Utils::Box(BOX_ANY , "%s",
+ "F1 - Press F1 again\n"
+ " for instructions\n"
+ "F2 - Sound on/off\n"
+ "F3 - Recall last line\n"
+ "F4 - Save game\n"
+ "F5 - Restore game\n"
+ "F6 - Inventory\n"
+ "F8 - Turbo button\n"
+ "F9 - Boss button\n\n"
+ "ESC - Return to game");
+}
+
+void Screen::drawStatusText() {
+ debugC(4, kDebugDisplay, "drawStatusText");
+
+ loadFont(U_FONT8);
+ uint16 sdx = stringLength(_vm._statusLine);
+ uint16 sdy = fontHeight() + 1; // + 1 for shadow
+ uint16 posX = 0;
+ uint16 posY = YPIX - sdy;
+
+ // Display the string and add rect to display list
+ writeStr(posX, posY, _vm._statusLine, _TLIGHTYELLOW);
+ displayList(D_ADD, posX, posY, sdx, sdy);
+
+ sdx = stringLength(_vm._scoreLine);
+ posY = 0;
+ writeStr(posX, posY, _vm._scoreLine, _TCYAN);
+ displayList(D_ADD, posX, posY, sdx, sdy);
+}
+
+void Screen::drawShape(int x, int y, int color1, int color2) {
+ for (int i = 0; i < shapeSize; i++) {
+ for (int j = 0; j < i; j++) {
+ _backBuffer[320 * (y + i) + (x + shapeSize + j - i)] = color1;
+ _frontBuffer[320 * (y + i) + (x + shapeSize + j - i)] = color1;
+ _backBuffer[320 * (y + i) + (x + shapeSize + j)] = color2;
+ _frontBuffer[320 * (y + i) + (x + shapeSize + j)] = color2;
+ _backBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j - i)] = color1;
+ _frontBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j - i)] = color1;
+ _backBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j)] = color2;
+ _frontBuffer[320 * (y + (2 * shapeSize - 1) - i) + (x + shapeSize + j)] = color2;
+ }
+ }
+}
+
+void Screen::drawRectangle(bool filledFl, uint16 x1, uint16 y1, uint16 x2, uint16 y2, int color) {
+ assert(x1 <= x2);
+ assert(y1 <= y2);
+
+ if (filledFl) {
+ for (int i = y1; i < y2; i++) {
+ for (int j = x1; j < x2; j++) {
+ _backBuffer[320 * i + j] = color;
+ _frontBuffer[320 * i + j] = color;
+ }
+ }
+ } else {
+ warning("STUB: drawRectangle()");
+ }
+}
+
+} // End of namespace Hugo
+
diff --git a/engines/hugo/display.h b/engines/hugo/display.h
new file mode 100644
index 0000000000..16dd87fc6f
--- /dev/null
+++ b/engines/hugo/display.h
@@ -0,0 +1,134 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_DISPLAY_H
+#define HUGO_DISPLAY_H
+
+namespace Hugo {
+#define shapeSize 24
+
+enum overlayState_t {UNDEF, FG, BG}; // Overlay state
+struct rect_t { // Rectangle used in Display list
+ int16 x; // Position in dib
+ int16 y; // Position in dib
+ int16 dx; // width
+ int16 dy; // height
+};
+
+class Screen {
+public:
+ Screen(HugoEngine &vm);
+ virtual ~Screen();
+
+ int16 fontHeight();
+ int16 stringLength(const char *s);
+
+ void displayBackground();
+ void displayFrame(int sx, int sy, seq_t *seq, bool foreFl);
+ void displayList(dupdate_t update, ...);
+ void displayRect(int16 x, int16 y, int16 dx, int16 dy);
+ void drawRectangle(bool filledFl, uint16 x1, uint16 y1, uint16 x2, uint16 y2, int color);
+ void drawShape(int x, int y, int color1, int color2);
+ void drawStatusText();
+ void initDisplay();
+ virtual void loadFont(int16 fontId) = 0;
+ void moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2);
+ void remapPal(uint16 oldIndex, uint16 newIndex);
+ void restorePal(Common::SeekableReadStream *f);
+ void savePal(Common::WriteStream *f);
+ void setBackgroundColor(long color);
+ void shadowStr(int16 sx, int16 sy, const char *s, byte color);
+ void userHelp();
+ void writeChr(int sx, int sy, byte color, char *local_fontdata);
+ void writeStr(int16 sx, int16 sy, const char *s, byte color);
+
+ icondib_t &getIconBuffer() {
+ return _iconBuffer;
+ }
+
+ viewdib_t &getBackBuffer() {
+ return _backBuffer;
+ }
+
+ viewdib_t &getBackBufferBackup() {
+ return _backBufferBackup;
+ }
+
+ viewdib_t &getFrontBuffer() {
+ return _frontBuffer;
+ }
+
+ viewdib_t &getGUIBuffer() {
+ return _GUIBuffer;
+ }
+
+protected:
+ HugoEngine &_vm;
+
+ // Fonts used in dib (non-GDI)
+ byte _fnt; // Current font number
+ byte _fontdata[NUM_FONTS][FONTSIZE]; // Font data
+ byte *_font[NUM_FONTS][FONT_LEN]; // Ptrs to each char
+
+private:
+ viewdib_t _frontBuffer;
+ viewdib_t _backBuffer;
+ viewdib_t _GUIBuffer; // User interface images
+ viewdib_t _backBufferBackup; // Backup _backBuffer during inventory
+ icondib_t _iconBuffer; // Inventory icon DIB
+
+ void createPal();
+ overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y);
+ void merge(rect_t *rectA, rect_t *rectB);
+ int16 mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax);
+ int16 center(const char *s);
+};
+
+class Screen_v1d : public Screen {
+public:
+ Screen_v1d(HugoEngine &vm);
+ ~Screen_v1d();
+
+ virtual void loadFont(int16 fontId);
+};
+
+class Screen_v1w : public Screen {
+public:
+ Screen_v1w(HugoEngine &vm);
+ ~Screen_v1w();
+
+ virtual void loadFont(int16 fontId);
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_DISPLAY_H
diff --git a/engines/hugo/display_v1d.cpp b/engines/hugo/display_v1d.cpp
new file mode 100644
index 0000000000..6cf20d413f
--- /dev/null
+++ b/engines/hugo/display_v1d.cpp
@@ -0,0 +1,83 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// Display.c - DIB related code for HUGOWIN
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+Screen_v1d::Screen_v1d(HugoEngine &vm) : Screen(vm) {
+}
+
+Screen_v1d::~Screen_v1d() {
+}
+
+// Load font file, construct font ptrs and reverse data bytes
+// TODO: This uses hardcoded fonts in hugo.dat, it should be replaced
+// by a proper implementation of .FON files
+void Screen_v1d::loadFont(int16 fontId) {
+ debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
+
+ static bool fontLoadedFl[NUM_FONTS] = {false, false, false};
+
+ _fnt = fontId - FIRST_FONT; // Set current font number
+
+ if (fontLoadedFl[_fnt]) // If already loaded, return
+ return;
+
+ fontLoadedFl[_fnt] = true;
+
+ memcpy(_fontdata[_fnt], _vm._arrayFont[_fnt], _vm._arrayFontSize[_fnt]);
+ _font[_fnt][0] = _fontdata[_fnt]; // Store height,width of fonts
+
+ int16 offset = 2; // Start at fontdata[2] ([0],[1] used for height,width)
+
+ // Setup the font array (127 characters)
+ for (int i = 1; i < 128; i++) {
+ _font[_fnt][i] = _fontdata[_fnt] + offset;
+ byte height = *(_fontdata[_fnt] + offset);
+ byte width = *(_fontdata[_fnt] + offset + 1);
+
+ int16 size = height * ((width + 7) >> 3);
+ for (int j = 0; j < size; j++)
+ Utils::reverseByte(&_fontdata[_fnt][offset + 2 + j]);
+
+ offset += 2 + size;
+ }
+}
+} // End of namespace Hugo
+
diff --git a/engines/hugo/display_v1w.cpp b/engines/hugo/display_v1w.cpp
new file mode 100644
index 0000000000..c98374dcde
--- /dev/null
+++ b/engines/hugo/display_v1w.cpp
@@ -0,0 +1,83 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// Display.c - DIB related code for HUGOWIN
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+Screen_v1w::Screen_v1w(HugoEngine &vm) : Screen(vm) {
+}
+
+Screen_v1w::~Screen_v1w() {
+}
+
+// Load font file, construct font ptrs and reverse data bytes
+void Screen_v1w::loadFont(int16 fontId) {
+ debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
+
+ static bool fontLoadedFl[NUM_FONTS] = {false, false, false};
+
+ _fnt = fontId - FIRST_FONT; // Set current font number
+
+ if (fontLoadedFl[_fnt]) // If already loaded, return
+ return;
+
+ fontLoadedFl[_fnt] = true;
+ _vm.file().readUIFItem(fontId, _fontdata[_fnt]);
+
+ // Compile font ptrs. Note: First ptr points to height,width of font
+ _font[_fnt][0] = _fontdata[_fnt]; // Store height,width of fonts
+
+ int16 offset = 2; // Start at fontdata[2] ([0],[1] used for height,width)
+
+ // Setup the font array (127 characters)
+ for (int i = 1; i < 128; i++) {
+ _font[_fnt][i] = _fontdata[_fnt] + offset;
+ byte height = *(_fontdata[_fnt] + offset);
+ byte width = *(_fontdata[_fnt] + offset + 1);
+
+ int16 size = height * ((width + 7) >> 3);
+ for (int j = 0; j < size; j++)
+ Utils::reverseByte(&_fontdata[_fnt][offset + 2 + j]);
+
+ offset += 2 + size;
+ }
+}
+} // End of namespace Hugo
+
diff --git a/engines/hugo/engine.cpp b/engines/hugo/engine.cpp
new file mode 100644
index 0000000000..d1d3e92cb2
--- /dev/null
+++ b/engines/hugo/engine.cpp
@@ -0,0 +1,968 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo 1-3 Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+#include "common/EventRecorder.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/engine.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+#define EDGE 10 // Closest object can get to edge of screen
+#define EDGE2 (EDGE * 2) // Push object further back on edge collision
+#define SHIFT 8 // Place hero this far inside bounding box
+#define MAX_OBJECTS 128 // Used in Update_images()
+#define BOUND(X, Y) ((_boundary[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0) // Boundary bit set
+
+config_t _config; // User's config
+maze_t _maze = {false, 0, 0, 0, 0, 0, 0, 0, 0};// Default to not in maze
+hugo_boot_t _boot; // Boot info structure file
+char _textBoxBuffer[MAX_BOX]; // Buffer for text box
+command_t _line = ""; // Line of user text input
+
+
+// Sets the playlist to be the default tune selection
+void HugoEngine::initPlaylist(bool playlist[MAX_TUNES]) {
+ debugC(1, kDebugEngine, "initPlaylist");
+
+ for (int16 i = 0; i < MAX_TUNES; i++)
+ playlist[i] = false;
+ for (int16 i = 0; _defltTunes[i] != -1; i++)
+ playlist[_defltTunes[i]] = true;
+}
+
+// Initialize the dynamic game status
+void HugoEngine::initStatus() {
+ debugC(1, kDebugEngine, "initStatus");
+ _status.initSaveFl = true; // Force initial save
+ _status.storyModeFl = false; // Not in story mode
+ _status.gameOverFl = false; // Hero not knobbled yet
+ _status.recordFl = false; // Not record mode
+ _status.playbackFl = false; // Not playback mode
+ _status.demoFl = false; // Not demo mode
+ _status.textBoxFl = false; // Not processing a text box
+// Strangerke - Not used ?
+// _status.mmtime = false; // Multimedia timer support
+ _status.lookFl = false; // Toolbar "look" button
+ _status.recallFl = false; // Toolbar "recall" button
+ _status.leftButtonFl = false; // Left mouse button pressed
+ _status.rightButtonFl = false; // Right mouse button pressed
+ _status.newScreenFl = false; // Screen not just loaded
+ _status.jumpExitFl = false; // Can't jump to a screen exit
+ _status.godModeFl = false; // No special cheats allowed
+ _status.helpFl = false; // Not calling WinHelp()
+ _status.doQuitFl = false;
+ _status.path[0] = 0; // Path to write files
+ _status.saveSlot = 0; // Slot to save/restore game
+ _status.screenWidth = 0; // Desktop screen width
+
+ // Initialize every start of new game
+ _status.tick = 0; // Tick count
+ _status.saveTick = 0; // Time of last save
+ _status.viewState = V_IDLE; // View state
+ _status.inventoryState = I_OFF; // Inventory icon bar state
+ _status.inventoryHeight = 0; // Inventory icon bar pos
+ _status.inventoryObjId = -1; // Inventory object selected (none)
+ _status.routeIndex = -1; // Hero not following a route
+ _status.go_for = GO_SPACE; // Hero walking to space
+ _status.go_id = -1; // Hero not walking to anything
+}
+
+// Initialize default config values. Must be done before Initialize().
+// Reset needed to save config.cx,cy which get splatted during OnFileNew()
+void HugoEngine::initConfig(inst_t action) {
+ debugC(1, kDebugEngine, "initConfig(%d)", action);
+
+ switch (action) {
+ case INSTALL:
+ _config.musicFl = true; // Music state initially on
+ _config.soundFl = true; // Sound state initially on
+ _config.turboFl = false; // Turbo state initially off
+ _config.backgroundMusicFl = false; // No music when inactive
+ _config.musicVolume = 85; // Music volume %
+ _config.soundVolume = 100; // Sound volume %
+ initPlaylist(_config.playlist); // Initialize default tune playlist
+
+ file().readBootFile(); // Read startup structure
+ break;
+ case RESET:
+ // Find first tune and play it
+ for (int16 i = 0; i < MAX_TUNES; i++) {
+ if (_config.playlist[i]) {
+ sound().playMusic(i);
+ break;
+ }
+ }
+
+ file().initSavedGame(); // Initialize saved game
+ break;
+ case RESTORE:
+ warning("Unhandled action RESTORE");
+ break;
+ }
+}
+
+void HugoEngine::initialize() {
+ debugC(1, kDebugEngine, "initialize");
+
+ sound().initSound();
+ scheduler().initEventQueue(); // Init scheduler stuff
+ screen().initDisplay(); // Create Dibs and palette
+ file().openDatabaseFiles(); // Open database files
+ calcMaxScore(); // Initialise maxscore
+
+ _rnd = new Common::RandomSource();
+ g_eventRec.registerRandomSource(*_rnd, "hugo");
+
+ _rnd->setSeed(42); // Kick random number generator
+
+ switch (getGameType()) {
+ case kGameTypeHugo1:
+ _episode = "\"HUGO'S HOUSE OF HORRORS\"";
+ _picDir = "";
+ break;
+ case kGameTypeHugo2:
+ _episode = "\"Hugo's Mystery Adventure\"";
+ _picDir = "hugo2/";
+ break;
+ case kGameTypeHugo3:
+ _episode = "\"Hugo's Amazon Adventure\"";
+ _picDir = "hugo3/";
+ break;
+ default:
+ error("Unknown game");
+ }
+}
+
+// Restore all resources before termination
+void HugoEngine::shutdown() {
+ debugC(1, kDebugEngine, "shutdown");
+
+ file().closeDatabaseFiles();
+ if (_status.recordFl || _status.playbackFl)
+ file().closePlaybackFile();
+ freeObjects();
+}
+
+void HugoEngine::readObjectImages() {
+ debugC(1, kDebugEngine, "readObjectImages");
+
+ for (int i = 0; i < _numObj; i++)
+ file().readImage(i, &_objects[i]);
+}
+
+// Read the uif image file (inventory icons)
+void HugoEngine::readUIFImages() {
+ debugC(1, kDebugEngine, "readUIFImages");
+
+ file().readUIFItem(UIF_IMAGES, screen().getGUIBuffer()); // Read all uif images
+}
+
+// Read scenery, overlay files for given screen number
+void HugoEngine::readScreenFiles(int screenNum) {
+ debugC(1, kDebugEngine, "readScreenFiles(%d)", screenNum);
+
+ file().readBackground(screenNum); // Scenery file
+ memcpy(screen().getBackBuffer(), screen().getFrontBuffer(), sizeof(screen().getFrontBuffer()));// Make a copy
+ file().readOverlay(screenNum, _boundary, BOUNDARY); // Boundary file
+ file().readOverlay(screenNum, _overlay, OVERLAY); // Overlay file
+ file().readOverlay(screenNum, _ovlBase, OVLBASE); // Overlay base file
+}
+
+// Update all object positions. Process object 'local' events
+// including boundary events and collisions
+void HugoEngine::moveObjects() {
+ debugC(4, kDebugEngine, "moveObjects");
+
+ // If route mode enabled, do special route processing
+ if (_status.routeIndex >= 0)
+ route().processRoute();
+
+ // Perform any adjustments to velocity based on special path types
+ // and store all (visible) object baselines into the boundary file.
+ // Don't store foreground or background objects
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if (obj->screenIndex == *_screen_p) {
+ switch (obj->pathType) {
+ case CHASE:
+ case CHASE2: {
+ int8 radius = obj->radius; // Default to object's radius
+ if (radius < 0) // If radius infinity, use closer value
+ radius = DX;
+
+ // Allowable motion wrt boundary
+ int dx = _hero->x + _hero->currImagePtr->x1 - obj->x - currImage->x1;
+ int dy = _hero->y + _hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+ if (abs(dx) <= radius)
+ obj->vx = 0;
+ else
+ obj->vx = (dx > 0) ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+ if (abs(dy) <= radius)
+ obj->vy = 0;
+ else
+ obj->vy = (dy > 0) ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+ // Set first image in sequence (if multi-seq object)
+ switch (obj->seqNumb) {
+ case 4:
+ if (!obj->vx) { // Got 4 directions
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dy >= 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (dx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ case 3:
+ case 2:
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (dx > 0) // Left & right only
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ break;
+ }
+
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ else {
+ obj->cycling = NOT_CYCLING;
+ boundaryCollision(obj); // Must have got hero!
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ break;
+ }
+ case WANDER2:
+ case WANDER:
+ if (!_rnd->getRandomNumber(3 * NORMAL_TPS)) { // Kick on random interval
+ obj->vx = _rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+ obj->vy = _rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+ // Set first image in sequence (if multi-seq object)
+ if (obj->seqNumb > 1) {
+ if (!obj->vx && (obj->seqNumb >= 4)) {
+ if (obj->vx != obj->oldvx) { // vx just stopped
+ if (obj->vy > 0)
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ }
+ } else if (obj->vx != obj->oldvx) {
+ if (obj->vx > 0)
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ else
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ }
+ }
+ obj->oldvx = obj->vx;
+ obj->oldvy = obj->vy;
+ currImage = obj->currImagePtr; // Get (new) ptr to current image
+ }
+ if (obj->vx || obj->vy)
+ obj->cycling = CYCLE_FORWARD;
+ break;
+ default:
+ ; // Really, nothing
+ }
+ // Store boundaries
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+ }
+ }
+
+ // Move objects, allowing for boundaries
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ if ((obj->screenIndex == *_screen_p) && (obj->vx || obj->vy)) {
+ // Only process if it's moving
+
+ // Do object movement. Delta_x,y return allowed movement in x,y
+ // to move as close to a boundary as possible without crossing it.
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ // object coordinates
+ int x1 = obj->x + currImage->x1; // Left edge of object
+ int x2 = obj->x + currImage->x2; // Right edge
+ int y1 = obj->y + currImage->y1; // Top edge
+ int y2 = obj->y + currImage->y2; // Bottom edge
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ clearBoundary(x1, x2, y2); // Clear our own boundary
+
+ // Allowable motion wrt boundary
+ int dx = deltaX(x1, x2, obj->vx, y2);
+ if (dx != obj->vx) {
+ // An object boundary collision!
+ boundaryCollision(obj);
+ obj->vx = 0;
+ }
+
+ int dy = deltaY(x1, x2, obj->vy, y2);
+ if (dy != obj->vy) {
+ // An object boundary collision!
+ boundaryCollision(obj);
+ obj->vy = 0;
+ }
+
+ if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ storeBoundary(x1, x2, y2); // Re-store our own boundary
+
+ obj->x += dx; // Update object position
+ obj->y += dy;
+
+ // Don't let object go outside screen
+ if (x1 < EDGE)
+ obj->x = EDGE2;
+ if (x2 > (XPIX - EDGE))
+ obj->x = XPIX - EDGE2 - (x2 - x1);
+ if (y1 < EDGE)
+ obj->y = EDGE2;
+ if (y2 > (YPIX - EDGE))
+ obj->y = YPIX - EDGE2 - (y2 - y1);
+
+ if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+
+ // Clear all object baselines from the boundary file.
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i]; // Get pointer to object
+ seq_t *currImage = obj->currImagePtr; // Get ptr to current image
+ if ((obj->screenIndex == *_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+ clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+ }
+
+ // If maze mode is enabled, do special maze processing
+ if (_maze.enabledFl)
+ processMaze();
+}
+
+// Return maximum allowed movement (from zero to vx) such that object does
+// not cross a boundary (either background or another object)
+int HugoEngine::deltaX(int x1, int x2, int vx, int y) {
+// Explanation of algorithm: The boundaries are drawn as contiguous
+// lines 1 pixel wide. Since DX,DY are not necessarily 1, we must
+// detect boundary crossing. If vx positive, examine each pixel from
+// x1 old to x2 new, else x2 old to x1 new, both at the y2 line.
+// If vx zero, no need to check. If vy non-zero then examine each
+// pixel on the line segment x1 to x2 from y old to y new.
+// Fix from Hugo I v1.5:
+// Note the diff is munged in the return statement to cater for a special
+// cases arising from differences in image widths from one sequence to
+// another. The problem occurs reversing direction at a wall where the
+// new image intersects before the object can move away. This is cured
+// by comparing the intersection with half the object width pos. If the
+// intersection is in the other half wrt the intended direction, use the
+// desired vx, else use the computed delta. i.e. believe the desired vx
+
+ debugC(3, kDebugEngine, "deltaX(%d, %d, %d, %d)", x1, x2, vx, y);
+
+ if (vx == 0)
+ return 0 ; // Object stationary
+
+ y *= XBYTES; // Offset into boundary file
+ if (vx > 0) {
+ // Moving to right
+ for (int i = x1 >> 3; i <= (x2 + vx) >> 3; i++) {// Search by byte
+ int b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]));
+ if (b < 8) { // b is index or 8
+ // Compute x of boundary and test if intersection
+ b += i << 3;
+ if ((b >= x1) && (b <= x2 + vx))
+ return (b < x1 + ((x2 - x1) >> 1)) ? vx : b - x2 - 1; // return dx
+ }
+ }
+ } else {
+ // Moving to left
+ for (int i = x2 >> 3; i >= (x1 + vx) >> 3; i--) {// Search by byte
+ int b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]));
+ if (b < 8) { // b is index or 8
+ // Compute x of boundary and test if intersection
+ b += i << 3;
+ if ((b >= x1 + vx) && (b <= x2))
+ return (b > x1 + ((x2 - x1) >> 1)) ? vx : b - x1 + 1; // return dx
+ }
+ }
+ }
+ return vx;
+}
+
+// Similar to Delta_x, but for movement in y direction. Special case of
+// bytes at end of line segment; must only count boundary bits falling on
+// line segment.
+int HugoEngine::deltaY(int x1, int x2, int vy, int y) {
+ debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y);
+
+ if (vy == 0)
+ return 0; // Object stationary
+
+ int inc = (vy > 0) ? 1 : -1;
+ for (int j = y + inc; j != (y + vy + inc); j += inc) { //Search by byte
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) {
+ int b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i];
+ if (b != 0) { // Any bit set
+ // Make sure boundary bits fall on line segment
+ if (i == (x2 >> 3)) // Adjust right end
+ b &= 0xff << ((i << 3) + 7 - x2);
+ else if (i == (x1 >> 3)) // Adjust left end
+ b &= 0xff >> (x1 - (i << 3));
+ if (b)
+ return j - y - inc;
+ }
+ }
+ }
+ return vy;
+}
+
+// Store a horizontal line segment in the object boundary file
+void HugoEngine::storeBoundary(int x1, int x2, int y) {
+ debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y);
+
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
+ byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
+ if (i == x2 >> 3) // Adjust right end
+ *b |= 0xff << ((i << 3) + 7 - x2);
+ else if (i == x1 >> 3) // Adjust left end
+ *b |= 0xff >> (x1 - (i << 3));
+ else
+ *b = 0xff;
+ }
+}
+
+// Clear a horizontal line segment in the object boundary file
+void HugoEngine::clearBoundary(int x1, int x2, int y) {
+ debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y);
+
+ for (int i = x1 >> 3; i <= x2 >> 3; i++) { // For each byte in line
+ byte *b = &_objBound[y * XBYTES + i]; // get boundary byte
+ if (i == x2 >> 3) // Adjust right end
+ *b &= ~(0xff << ((i << 3) + 7 - x2));
+ else if (i == x1 >> 3) // Adjust left end
+ *b &= ~(0xff >> (x1 - (i << 3)));
+ else
+ *b = 0;
+ }
+}
+
+// Maze mode is enabled. Check to see whether hero has crossed the maze
+// bounding box, if so, go to the next room */
+void HugoEngine::processMaze() {
+ debugC(1, kDebugEngine, "processMaze");
+
+ seq_t *currImage = _hero->currImagePtr; // Get ptr to current image
+
+ // hero coordinates
+ int x1 = _hero->x + currImage->x1; // Left edge of object
+ int x2 = _hero->x + currImage->x2; // Right edge
+ int y1 = _hero->y + currImage->y1; // Top edge
+ int y2 = _hero->y + currImage->y2; // Bottom edge
+
+ if (x1 < _maze.x1) {
+ // Exit west
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - 1;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x2 - SHIFT - (x2 - x1);
+ _actListArr[_alNewscrIndex][0].a2.y = _hero->y;
+ _status.routeIndex = -1;
+ scheduler().insertActionList(_alNewscrIndex);
+ } else if (x2 > _maze.x2) {
+ // Exit east
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + 1;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x1 + SHIFT;
+ _actListArr[_alNewscrIndex][0].a2.y = _hero->y;
+ _status.routeIndex = -1;
+ scheduler().insertActionList(_alNewscrIndex);
+ } else if (y1 < _maze.y1 - SHIFT) {
+ // Exit north
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - _maze.size;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x3;
+ _actListArr[_alNewscrIndex][0].a2.y = _maze.y2 - SHIFT - (y2 - y1);
+ _status.routeIndex = -1;
+ scheduler().insertActionList(_alNewscrIndex);
+ } else if (y2 > _maze.y2 - SHIFT / 2) {
+ // Exit south
+ _actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + _maze.size;
+ _actListArr[_alNewscrIndex][0].a2.x = _maze.x4;
+ _actListArr[_alNewscrIndex][0].a2.y = _maze.y1 + SHIFT;
+ _status.routeIndex = -1;
+ scheduler().insertActionList(_alNewscrIndex);
+ }
+}
+
+// Compare function for the quicksort. The sort is to order the objects in
+// increasing vertical position, using y+y2 as the baseline
+// Returns -1 if ay2 < by2 else 1 if ay2 > by2 else 0
+int HugoEngine::y2comp(const void *a, const void *b) {
+ debugC(6, kDebugEngine, "y2comp");
+
+ const object_t *p1 = &s_Engine->_objects[*(const byte *)a];
+ const object_t *p2 = &s_Engine->_objects[*(const byte *)b];
+
+ if (p1 == p2)
+ // Why does qsort try the same indexes?
+ return 0;
+
+ if (p1->priority == BACKGROUND)
+ return -1;
+
+ if (p2->priority == BACKGROUND)
+ return 1;
+
+ if (p1->priority == FOREGROUND)
+ return 1;
+
+ if (p2->priority == FOREGROUND)
+ return -1;
+
+ int ay2 = p1->y + p1->currImagePtr->y2;
+ int by2 = p2->y + p2->currImagePtr->y2;
+
+ return ay2 - by2;
+}
+
+// Draw all objects on screen as follows:
+// 1. Sort 'FLOATING' objects in order of y2 (base of object)
+// 2. Display new object frames/positions in dib
+// Finally, cycle any animating objects to next frame
+void HugoEngine::updateImages() {
+ debugC(5, kDebugEngine, "updateImages");
+
+ // Initialise the index array to visible objects in current screen
+ int num_objs = 0;
+ byte objindex[MAX_OBJECTS]; // Array of indeces to objects
+
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ if ((obj->screenIndex == *_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
+ objindex[num_objs++] = i;
+ }
+
+ // Sort the objects into increasing y+y2 (painter's algorithm)
+ qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
+
+ // Add each visible object to display list
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ // Count down inter-frame timer
+ if (obj->frameTimer)
+ obj->frameTimer--;
+
+ if (obj->cycling > ALMOST_INVISIBLE) { // Only if visible
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_FORWARD:
+ if (obj->frameTimer) // Not time to see next frame yet
+ screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+ else
+ screen().displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL);
+ break;
+ case CYCLE_BACKWARD: {
+ seq_t *seqPtr = obj->currImagePtr;
+ if (!obj->frameTimer) { // Show next frame
+ while (seqPtr->nextSeqPtr != obj->currImagePtr)
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+ screen().displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ // Cycle any animating objects
+ for (int i = 0; i < num_objs; i++) {
+ object_t *obj = &_objects[objindex[i]];
+ if (obj->cycling != INVISIBLE) {
+ // Only if it's visible
+ if (obj->cycling == ALMOST_INVISIBLE)
+ obj->cycling = INVISIBLE;
+
+ // Now Rotate to next picture in sequence
+ switch (obj->cycling) {
+ case NOT_CYCLING:
+ break;
+ case CYCLE_FORWARD:
+ if (!obj->frameTimer) {
+ // Time to step to next frame
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is last frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb) { // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CYCLE_BACKWARD: {
+ if (!obj->frameTimer) {
+ // Time to step to prev frame
+ seq_t *seqPtr = obj->currImagePtr;
+ while (obj->currImagePtr->nextSeqPtr != seqPtr)
+ obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+ // Find out if this is first frame of sequence
+ // If so, reset frame_timer and decrement n_cycle
+ if (obj->frameInterval || obj->cycleNumb) {
+ obj->frameTimer = obj->frameInterval;
+ for (int j = 0; j < obj->seqNumb; j++) {
+ if (obj->currImagePtr == obj->seqList[j].seqPtr) {
+ if (obj->cycleNumb){ // Decr cycleNumb if Non-continous
+ if (!--obj->cycleNumb)
+ obj->cycling = NOT_CYCLING;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ obj->oldx = obj->x;
+ obj->oldy = obj->y;
+ }
+ }
+}
+
+// Return object index of the topmost object under the cursor, or -1 if none
+// Objects are filtered if not "useful"
+int16 HugoEngine::findObject(uint16 x, uint16 y) {
+ debugC(3, kDebugEngine, "findObject(%d, %d)", x, y);
+
+ int16 objIndex = -1; // Index of found object
+ uint16 y2Max = 0; // Greatest y2
+ object_t *obj = _objects;
+ // Check objects on screen
+ for (int i = 0; i < _numObj; i++, obj++) {
+ // Object must be in current screen and "useful"
+ if (obj->screenIndex == *_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) {
+ seq_t *curImage = obj->currImagePtr;
+ // Object must have a visible image...
+ if (curImage != 0 && obj->cycling != INVISIBLE) {
+ // If cursor inside object
+ if (x >= (uint16)obj->x && x <= obj->x + curImage->x2 && y >= (uint16)obj->y && y <= obj->y + curImage->y2) {
+ // If object is closest so far
+ if (obj->y + curImage->y2 > y2Max) {
+ y2Max = obj->y + curImage->y2;
+ objIndex = i; // Found an object!
+ }
+ }
+ } else {
+ // ...or a dummy object that has a hotspot rectangle
+ if (curImage == 0 && obj->vxPath != 0 && !obj->carriedFl) {
+ // If cursor inside special rectangle
+ if ((int16)x >= obj->oldx && (int16)x < obj->oldx + obj->vxPath && (int16)y >= obj->oldy && (int16)y < obj->oldy + obj->vyPath) {
+ // If object is closest so far
+ if (obj->oldy + obj->vyPath - 1 > (int16)y2Max) {
+ y2Max = obj->oldy + obj->vyPath - 1;
+ objIndex = i; // Found an object!
+ }
+ }
+ }
+ }
+ }
+ }
+ return objIndex;
+}
+
+// Find a clear space around supplied object that hero can walk to
+bool HugoEngine::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) {
+ debugC(1, kDebugEngine, "findObjectSpace(obj, %d, %d)", *destx, *desty);
+
+ seq_t *curImage = obj->currImagePtr;
+ int16 y = obj->y + curImage->y2 - 1;
+
+ bool foundFl = true;
+ // Try left rear corner
+ for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+
+ if (!foundFl) { // Try right rear corner
+ foundFl = true;
+ for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ if (!foundFl) { // Try left front corner
+ foundFl = true;
+ y += 2;
+ for (int16 x = *destx = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ if (!foundFl) { // Try right rear corner
+ foundFl = true;
+ for (int16 x = *destx = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) {
+ if (BOUND(x, y))
+ foundFl = false;
+ }
+ }
+
+ *desty = y;
+ return foundFl;
+}
+
+// Search background command list for this screen for supplied object.
+// Return first associated verb (not "look") or 0 if none found.
+char *HugoEngine::useBG(char *name) {
+ debugC(1, kDebugEngine, "useBG(%s)", name);
+
+ objectList_t p = _backgroundObjects[*_screen_p];
+ for (int i = 0; *_arrayVerbs[p[i].verbIndex]; i++) {
+ if ((name == _arrayNouns[p[i].nounIndex][0] &&
+ p[i].verbIndex != _look) &&
+ ((p[i].roomState == DONT_CARE) || (p[i].roomState == _screenStates[*_screen_p])))
+ return _arrayVerbs[p[i].verbIndex][0];
+ }
+
+ return 0;
+}
+
+// If status.objid = -1, pick up objid, else use status.objid on objid,
+// if objid can't be picked up, use it directly
+void HugoEngine::useObject(int16 objId) {
+ debugC(1, kDebugEngine, "useObject(%d)", objId);
+
+ char *verb; // Background verb to use directly
+ object_t *obj = &_objects[objId]; // Ptr to object
+ if (_status.inventoryObjId == -1) {
+ // Get or use objid directly
+ if ((obj->genericCmd & TAKE) || obj->objValue) // Get collectible item
+ sprintf(_line, "%s %s", _arrayVerbs[_take][0], _arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & LOOK) // Look item
+ sprintf(_line, "%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]);
+ else if (obj->genericCmd & DROP) // Drop item
+ sprintf(_line, "%s %s", _arrayVerbs[_drop][0], _arrayNouns[obj->nounIndex][0]);
+ else if (obj->cmdIndex != 0) // Use non-collectible item if able
+ sprintf(_line, "%s %s", _arrayVerbs[_cmdList[obj->cmdIndex][1].verbIndex][0], _arrayNouns[obj->nounIndex][0]);
+ else if ((verb = useBG(_arrayNouns[obj->nounIndex][0])) != 0)
+ sprintf(_line, "%s %s", verb, _arrayNouns[obj->nounIndex][0]);
+ else
+ return; // Can't use object directly
+ } else {
+ // Use status.objid on objid
+ // Default to first cmd verb
+ sprintf(_line, "%s %s %s", _arrayVerbs[_cmdList[_objects[_status.inventoryObjId].cmdIndex][1].verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
+
+ // Check valid use of objects and override verb if necessary
+ for (uses_t *use = _uses; use->objId != _numObj; use++) {
+ if (_status.inventoryObjId == use->objId) {
+ // Look for secondary object, if found use matching verb
+ bool foundFl = false;
+ for (target_t *target = use->targets; _arrayNouns[target->nounIndex] != 0; target++)
+ if (_arrayNouns[target->nounIndex][0] == _arrayNouns[obj->nounIndex][0]) {
+ foundFl = true;
+ sprintf(_line, "%s %s %s", _arrayVerbs[target->verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
+ }
+
+ // No valid use of objects found, print failure string
+ if (!foundFl) {
+ // Deselect dragged icon if inventory not active
+ if (_status.inventoryState != I_ACTIVE)
+ _status.inventoryObjId = -1;
+ Utils::Box(BOX_ANY, "%s", _textData[use->dataIndex]);
+ return;
+ }
+ }
+ }
+ }
+
+ if (_status.inventoryState == I_ACTIVE) // If inventory active, remove it
+ _status.inventoryState = I_UP;
+ _status.inventoryObjId = -1; // Deselect any dragged icon
+ parser().lineHandler(); // and process command
+}
+
+// Issue "Look at <object>" command
+// Note special case of swapped hero image
+void HugoEngine::lookObject(object_t *obj) {
+ debugC(1, kDebugEngine, "lookObject");
+
+ if (obj == _hero)
+ // Hero swapped - look at other
+ obj = &_objects[_heroImage];
+
+ parser().command("%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]);
+}
+
+// Free all object images
+void HugoEngine::freeObjects() {
+ debugC(1, kDebugEngine, "freeObjects");
+
+ // Nothing to do if not allocated yet
+ if (_hero->seqList[0].seqPtr == 0)
+ return;
+
+ // Free all sequence lists and image data
+ for (int i = 0; i < _numObj; i++) {
+ object_t *obj = &_objects[i];
+ for (int j = 0; j < obj->seqNumb; j++) { // for each sequence
+ seq_t *seq = obj->seqList[j].seqPtr; // Free image
+ if (seq == 0) // Failure during database load
+ break;
+ do {
+ free(seq->imagePtr);
+ seq = seq->nextSeqPtr;
+ } while (seq != obj->seqList[j].seqPtr);
+ free(seq); // Free sequence record
+ }
+ }
+}
+
+// Add action lists for this screen to event queue
+void HugoEngine::screenActions(int screenNum) {
+ debugC(1, kDebugEngine, "screenActions(%d)", screenNum);
+
+ uint16 *screenAct = _screenActs[screenNum];
+ if (screenAct) {
+ for (int i = 0; screenAct[i]; i++)
+ scheduler().insertActionList(screenAct[i]);
+ }
+}
+
+// Set the new screen number into the hero object and any carried objects
+void HugoEngine::setNewScreen(int screenNum) {
+ debugC(1, kDebugEngine, "setNewScreen(%d)", screenNum);
+
+ *_screen_p = screenNum; // HERO object
+ for (int i = HERO + 1; i < _numObj; i++) { // Any others
+ if (_objects[i].carriedFl) // being carried
+ _objects[i].screenIndex = screenNum;
+ }
+}
+
+// An object has collided with a boundary. See if any actions are required
+void HugoEngine::boundaryCollision(object_t *obj) {
+ debugC(1, kDebugEngine, "boundaryCollision");
+
+ if (obj == _hero) {
+ // Hotspots only relevant to HERO
+ int x;
+ if (obj->vx > 0)
+ x = obj->x + obj->currImagePtr->x2;
+ else
+ x = obj->x + obj->currImagePtr->x1;
+ int y = obj->y + obj->currImagePtr->y2;
+
+ for (int i = 0; _hotspots[i].screenIndex >= 0; i++) {
+ hotspot_t *hotspot = &_hotspots[i];
+ if (hotspot->screenIndex == obj->screenIndex)
+ if ((x >= hotspot->x1) && (x <= hotspot->x2) && (y >= hotspot->y1) && (y <= hotspot->y2)) {
+ scheduler().insertActionList(hotspot->actIndex);
+ break;
+ }
+ }
+ } else {
+ // Check whether an object collided with HERO
+ int dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1;
+ int dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2;
+ // If object's radius is infinity, use a closer value
+ int8 radius = obj->radius;
+ if (radius < 0)
+ radius = DX * 2;
+ if ((abs(dx) <= radius) && (abs(dy) <= radius))
+ scheduler().insertActionList(obj->actIndex);
+ }
+}
+
+// Initialize screen components and display results
+void HugoEngine::initNewScreenDisplay() {
+ debugC(1, kDebugEngine, "initNewScreenDisplay");
+
+ screen().displayList(D_INIT);
+ screen().setBackgroundColor(_TBLACK);
+ screen().displayBackground();
+
+ // Stop premature object display in Display_list(D_DISPLAY)
+ _status.newScreenFl = true;
+}
+
+// Add up all the object values and all the bonus points
+void HugoEngine::calcMaxScore() {
+ debugC(1, kDebugEngine, "calcMaxScore");
+
+ for (int i = 0; i < _numObj; i++)
+ _maxscore += _objects[i].objValue;
+
+ for (int i = 0; i < _numBonuses; i++)
+ _maxscore += _points[i].score;
+}
+
+// Exit game, advertise trilogy, show copyright
+void HugoEngine::endGame() {
+ debugC(1, kDebugEngine, "endGame");
+
+ if (!_boot.registered)
+ Utils::Box(BOX_ANY, "%s", _textEngine[kEsAdvertise]);
+ Utils::Box(BOX_ANY, "%s\n%s", _episode, COPYRIGHT);
+ _status.viewState = V_EXIT;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/engine.h b/engines/hugo/engine.h
new file mode 100644
index 0000000000..0d14d244b5
--- /dev/null
+++ b/engines/hugo/engine.h
@@ -0,0 +1,44 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo 1-3 Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_ENGINE_H
+#define HUGO_ENGINE_H
+namespace Hugo {
+
+enum seqTextEngine {
+ // Strings used by the engine
+ kEsAdvertise = 0
+};
+
+} // End of namespace Hugo
+
+#endif // HUGO_ENGINE_H
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
new file mode 100644
index 0000000000..fa8d5b9947
--- /dev/null
+++ b/engines/hugo/file.cpp
@@ -0,0 +1,676 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/global.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager::FileManager(HugoEngine &vm) : _vm(vm) {
+}
+
+FileManager::~FileManager() {
+}
+
+byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) {
+// Convert 4 planes (RGBI) data to 8-bit DIB format
+// Return original plane data ptr
+ debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
+
+ dataPtr += y * bpl * 8; // Point to correct DIB line
+ for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes
+ for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte
+ *dataPtr++ = (((p[r] >> bit & 1) << 0) |
+ ((p[g] >> bit & 1) << 1) |
+ ((p[b] >> bit & 1) << 2) |
+ ((p[i] >> bit & 1) << 3));
+ }
+ }
+ return p;
+}
+
+seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name) {
+// Read a pcx file of length len. Use supplied seq_p and image_p or
+// allocate space if NULL. Name used for errors. Returns address of seq_p
+// Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
+ debugC(1, kDebugFile, "readPCX(..., %s)", name);
+
+ // Read in the PCC header and check consistency
+ static PCC_header_t PCC_header;
+ PCC_header.mfctr = f.readByte();
+ PCC_header.vers = f.readByte();
+ PCC_header.enc = f.readByte();
+ PCC_header.bpx = f.readByte();
+ PCC_header.x1 = f.readUint16LE();
+ PCC_header.y1 = f.readUint16LE();
+ PCC_header.x2 = f.readUint16LE();
+ PCC_header.y2 = f.readUint16LE();
+ PCC_header.xres = f.readUint16LE();
+ PCC_header.yres = f.readUint16LE();
+ f.read(PCC_header.palette, sizeof(PCC_header.palette));
+ PCC_header.vmode = f.readByte();
+ PCC_header.planes = f.readByte();
+ PCC_header.bytesPerLine = f.readUint16LE();
+ f.read(PCC_header.fill2, sizeof(PCC_header.fill2));
+
+ if (PCC_header.mfctr != 10)
+ Utils::Error(PCCH_ERR, "%s", name);
+
+ // Allocate memory for seq_t if 0
+ if (seqPtr == 0) {
+ if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == 0)
+ Utils::Error(HEAP_ERR, "%s", name);
+ }
+
+ // Find size of image data in 8-bit DIB format
+ // Note save of x2 - marks end of valid data before garbage
+ uint16 bytesPerLine4 = PCC_header.bytesPerLine * 4; // 4-bit bpl
+ seqPtr->bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl
+ seqPtr->lines = PCC_header.y2 - PCC_header.y1 + 1;
+ seqPtr->x2 = PCC_header.x2 - PCC_header.x1 + 1;
+ // Size of the image
+ uint16 size = seqPtr->lines * seqPtr->bytesPerLine8;
+
+ // Allocate memory for image data if NULL
+ if (imagePtr == 0) {
+ if ((imagePtr = (byte *)malloc((size_t) size)) == 0)
+ Utils::Error(HEAP_ERR, "%s", name);
+ }
+
+ seqPtr->imagePtr = imagePtr;
+
+ // Process the image data, converting to 8-bit DIB format
+ uint16 y = 0; // Current line index
+ byte pline[XPIX]; // Hold 4 planes of data
+ byte *p = pline; // Ptr to above
+ while (y < seqPtr->lines) {
+ byte c = f.readByte();
+ if ((c & REP_MASK) == REP_MASK) {
+ byte d = f.readByte(); // Read data byte
+ for (int i = 0; i < (c & LEN_MASK); i++) {
+ *p++ = d;
+ if ((uint16)(p - pline) == bytesPerLine4)
+ p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+ }
+ } else {
+ *p++ = c;
+ if ((uint16)(p - pline) == bytesPerLine4)
+ p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+ }
+ }
+ return seqPtr;
+}
+
+void FileManager::readImage(int objNum, object_t *objPtr) {
+// Read object file of PCC images into object supplied
+ debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum);
+
+ if (!objPtr->seqNumb) // This object has no images
+ return;
+
+ if (_vm.isPacked()) {
+ _objectsArchive.seek((uint32)objNum * sizeof(objBlock_t), SEEK_SET);
+
+ objBlock_t objBlock; // Info on file within database
+ objBlock.objOffset = _objectsArchive.readUint32LE();
+ objBlock.objLength = _objectsArchive.readUint32LE();
+
+ _objectsArchive.seek(objBlock.objOffset, SEEK_SET);
+ } else {
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+ strcat(strcat(strcpy(buf, _vm._picDir), _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ if (!_objectsArchive.open(buf)) {
+ warning("File %s not found, trying again with %s%s", buf, _vm._arrayNouns[objPtr->nounIndex][0], OBJEXT);
+ strcat(strcpy(buf, _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ if (!_objectsArchive.open(buf))
+ Utils::Error(FILE_ERR, "%s", buf);
+ }
+ }
+
+ bool firstFl = true; // Initializes pcx read function
+ seq_t *seqPtr = 0; // Ptr to sequence structure
+
+ // Now read the images into an images list
+ for (int j = 0; j < objPtr->seqNumb; j++) { // for each sequence
+ for (int k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
+ if (k == 0) { // First image
+ // Read this image - allocate both seq and image memory
+ seqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ objPtr->seqList[j].seqPtr = seqPtr;
+ firstFl = false;
+ } else { // Subsequent image
+ // Read this image - allocate both seq and image memory
+ seqPtr->nextSeqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+
+ // Compute the bounding box - x1, x2, y1, y2
+ // Note use of x2 - marks end of valid data in row
+ uint16 x2 = seqPtr->x2;
+ seqPtr->x1 = seqPtr->x2;
+ seqPtr->x2 = 0;
+ seqPtr->y1 = seqPtr->lines;
+ seqPtr->y2 = 0;
+
+ image_pt dibPtr = seqPtr->imagePtr;
+ for (int y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2) {
+ for (int x = 0; x < x2; x++) {
+ if (*dibPtr++) { // Some data found
+ if (x < seqPtr->x1)
+ seqPtr->x1 = x;
+ if (x > seqPtr->x2)
+ seqPtr->x2 = x;
+ if (y < seqPtr->y1)
+ seqPtr->y1 = y;
+ if (y > seqPtr->y2)
+ seqPtr->y2 = y;
+ }
+ }
+ }
+ }
+ seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head
+ }
+
+ // Set the current image sequence to first or last
+ switch (objPtr->cycling) {
+ case INVISIBLE: // (May become visible later)
+ case ALMOST_INVISIBLE:
+ case NOT_CYCLING:
+ case CYCLE_FORWARD:
+ objPtr->currImagePtr = objPtr->seqList[0].seqPtr;
+ break;
+ case CYCLE_BACKWARD:
+ objPtr->currImagePtr = seqPtr;
+ break;
+ default:
+ warning("Unexpected cycling: %d", objPtr->cycling);
+ }
+
+ if (!_vm.isPacked())
+ _objectsArchive.close();
+}
+
+sound_pt FileManager::getSound(int16 sound, uint16 *size) {
+// Read sound (or music) file data. Call with SILENCE to free-up
+// any allocated memory. Also returns size of data
+ debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
+
+ // No more to do if SILENCE (called for cleanup purposes)
+ if (sound == _vm._soundSilence)
+ return 0;
+
+ // Open sounds file
+ Common::File fp; // Handle to SOUND_FILE
+ if (!fp.open(SOUND_FILE)) {
+ warning("Hugo Error: File not found %s", SOUND_FILE);
+ return 0;
+ }
+
+ // If this is the first call, read the lookup table
+ static bool has_read_header = false;
+ static sound_hdr_t s_hdr[MAX_SOUNDS]; // Sound lookup table
+
+ if (!has_read_header) {
+ if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr))
+ Utils::Error(FILE_ERR, "%s", SOUND_FILE);
+ has_read_header = true;
+ }
+
+ *size = s_hdr[sound].size;
+ if (*size == 0)
+ Utils::Error(SOUND_ERR, "%s", SOUND_FILE);
+
+ // Allocate memory for sound or music, if possible
+ sound_pt soundPtr = (byte *)malloc(s_hdr[sound].size); // Ptr to sound data
+ if (soundPtr == 0) {
+ Utils::Warn("%s", "Low on memory");
+ return 0;
+ }
+
+ // Seek to data and read it
+ fp.seek(s_hdr[sound].offset, SEEK_SET);
+ if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size)
+ Utils::Error(FILE_ERR, "%s", SOUND_FILE);
+
+ fp.close();
+
+ return soundPtr;
+}
+
+bool FileManager::fileExists(char *filename) {
+// Return whether file exists or not
+ Common::File f;
+ if (f.open(filename)) {
+ f.close();
+ return true;
+ }
+ return false;
+}
+
+void FileManager::saveSeq(object_t *obj) {
+// Save sequence number and image number in given object
+ debugC(1, kDebugFile, "saveSeq");
+
+ bool found = false;
+ for (int j = 0; !found && (j < obj->seqNumb); j++) {
+ seq_t *q = obj->seqList[j].seqPtr;
+ for (int k = 0; !found && (k < obj->seqList[j].imageNbr); k++) {
+ if (obj->currImagePtr == q) {
+ found = true;
+ obj->curSeqNum = j;
+ obj->curImageNum = k;
+ } else {
+ q = q->nextSeqPtr;
+ }
+ }
+ }
+}
+
+void FileManager::restoreSeq(object_t *obj) {
+// Set up cur_seq_p from stored sequence and image number in object
+ debugC(1, kDebugFile, "restoreSeq");
+
+ seq_t *q = obj->seqList[obj->curSeqNum].seqPtr;
+ for (int j = 0; j < obj->curImageNum; j++)
+ q = q->nextSeqPtr;
+ obj->currImagePtr = q;
+}
+
+void FileManager::saveGame(int16 slot, const char *descrip) {
+// Save game to supplied slot (-1 is INITFILE)
+ debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
+
+ // Get full path of saved game file - note test for INITFILE
+ Common::String path; // Full path of saved game
+
+ if (slot == -1)
+ path = _vm._initFilename;
+ else
+ path = Common::String::printf(_vm._saveFilename.c_str(), slot);
+
+ Common::WriteStream *out = _vm.getSaveFileManager()->openForSaving(path);
+ if (!out) {
+ warning("Can't create file '%s', game not saved", path.c_str());
+ return;
+ }
+
+ // Write version. We can't restore from obsolete versions
+ out->write(&kSavegameVersion, sizeof(kSavegameVersion));
+
+ // Save description of saved game
+ out->write(descrip, DESCRIPLEN);
+
+ // Save objects
+ for (int i = 0; i < _vm._numObj; i++) {
+ // Save where curr_seq_p is pointing to
+ saveSeq(&_vm._objects[i]);
+ out->write(&_vm._objects[i], sizeof(object_t));
+ }
+
+ const status_t &gameStatus = _vm.getGameStatus();
+
+ // Save whether hero image is swapped
+ out->write(&_vm._heroImage, sizeof(_vm._heroImage));
+
+ // Save score
+ int score = _vm.getScore();
+ out->write(&score, sizeof(score));
+
+ // Save story mode
+ out->write(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+
+ // Save jumpexit mode
+ out->write(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+
+ // Save gameover status
+ out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+
+ // Save screen states
+ out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+ // Save points table
+ out->write(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+ // Now save current time and all current events in event queue
+ _vm.scheduler().saveEvents(out);
+
+ // Save palette table
+ _vm.screen().savePal(out);
+
+ // Save maze status
+ out->write(&_maze, sizeof(maze_t));
+
+ out->finalize();
+
+ delete out;
+}
+
+void FileManager::restoreGame(int16 slot) {
+// Restore game from supplied slot number (-1 is INITFILE)
+ debugC(1, kDebugFile, "restoreGame(%d)", slot);
+
+ // Initialize new-game status
+ _vm.initStatus();
+
+ // Get full path of saved game file - note test for INITFILE
+ Common::String path; // Full path of saved game
+
+ if (slot == -1)
+ path = _vm._initFilename;
+ else
+ path = Common::String::printf(_vm._saveFilename.c_str(), slot);
+
+ Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(path);
+ if (!in)
+ return;
+
+ // Check version, can't restore from different versions
+ int saveVersion;
+ in->read(&saveVersion, sizeof(saveVersion));
+ if (saveVersion != kSavegameVersion) {
+ Utils::Error(GEN_ERR, "%s", "Savegame of incompatible version");
+ return;
+ }
+
+ // Skip over description
+ in->seek(DESCRIPLEN, SEEK_CUR);
+
+ // If hero image is currently swapped, swap it back before restore
+ if (_vm._heroImage != HERO)
+ _vm.scheduler().swapImages(HERO, _vm._heroImage);
+
+ // Restore objects, retain current seqList which points to dynamic mem
+ // Also, retain cmnd_t pointers
+ for (int i = 0; i < _vm._numObj; i++) {
+ object_t *p = &_vm._objects[i];
+ seqList_t seqList[MAX_SEQUENCES];
+ memcpy(seqList, p->seqList, sizeof(seqList_t));
+ uint16 cmdIndex = p->cmdIndex;
+ in->read(p, sizeof(object_t));
+ p->cmdIndex = cmdIndex;
+ memcpy(p->seqList, seqList, sizeof(seqList_t));
+ }
+
+ in->read(&_vm._heroImage, sizeof(_vm._heroImage));
+
+ // If hero swapped in saved game, swap it
+ int heroImg = _vm._heroImage;
+ if (heroImg != HERO)
+ _vm.scheduler().swapImages(HERO, _vm._heroImage);
+ _vm._heroImage = heroImg;
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ int score;
+ in->read(&score, sizeof(score));
+ _vm.setScore(score);
+
+ in->read(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+ in->read(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+ in->read(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+ in->read(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+ // Restore points table
+ in->read(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+ // Restore ptrs to currently loaded objects
+ for (int i = 0; i < _vm._numObj; i++)
+ restoreSeq(&_vm._objects[i]);
+
+ // Now restore time of the save and the event queue
+ _vm.scheduler().restoreEvents(in);
+
+ // Restore palette and change it if necessary
+ _vm.screen().restorePal(in);
+
+ // Restore maze status
+ in->read(&_maze, sizeof(maze_t));
+
+ delete in;
+}
+
+void FileManager::initSavedGame() {
+// Initialize the size of a saved game (from the fixed initial game).
+// If status.initsave is TRUE, or the initial saved game is not found,
+// force a save to create one. Normally the game will be shipped with
+// the initial game file but useful to force a write during development
+// when the size is changeable.
+// The net result is a valid INITFILE, with status.savesize initialized.
+ debugC(1, kDebugFile, "initSavedGame");
+
+ // Force save of initial game
+ if (_vm.getGameStatus().initSaveFl)
+ saveGame(-1, "");
+
+ // If initial game doesn't exist, create it
+ Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
+ if (!in) {
+ saveGame(-1, "");
+ in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
+ if (!in) {
+ Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
+ return;
+ }
+ }
+
+ // Must have an open saved game now
+ _vm.getGameStatus().saveSize = in->size();
+ delete in;
+
+ // Check sanity - maybe disk full or path set to read-only drive?
+ if (_vm.getGameStatus().saveSize == -1)
+ Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
+}
+
+void FileManager::openPlaybackFile(bool playbackFl, bool recordFl) {
+ debugC(1, kDebugFile, "openPlaybackFile(%d, %d)", (playbackFl) ? 1 : 0, (recordFl) ? 1 : 0);
+
+ if (playbackFl) {
+ if (!(fpb = fopen(PBFILE, "r+b")))
+ Utils::Error(FILE_ERR, "%s", PBFILE);
+ } else if (recordFl) {
+ fpb = fopen(PBFILE, "wb");
+ }
+ pbdata.time = 0; // Say no key available
+}
+
+void FileManager::closePlaybackFile() {
+ fclose(fpb);
+}
+
+void FileManager::printBootText() {
+// Read the encrypted text from the boot file and print it
+ debugC(1, kDebugFile, "printBootText");
+
+ Common::File ofp;
+ if (!ofp.open(BOOTFILE)) {
+ if (_vm._gameVariant == 3) {
+ //TODO initialize properly _boot structure
+ warning("printBootText - Skipping as H1 Dos may be a freeware");
+ return;
+ } else {
+ Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ }
+ }
+
+ // Allocate space for the text and print it
+ char *buf = (char *)malloc(_boot.exit_len + 1);
+ if (buf) {
+ // Skip over the boot structure (already read) and read exit text
+ ofp.seek((long)sizeof(_boot), SEEK_SET);
+ if (ofp.read(buf, _boot.exit_len) != (size_t)_boot.exit_len)
+ Utils::Error(FILE_ERR, "%s", BOOTFILE);
+
+ // Decrypt the exit text, using CRYPT substring
+ int i;
+ for (i = 0; i < _boot.exit_len; i++)
+ buf[i] ^= CRYPT[i % strlen(CRYPT)];
+
+ buf[i] = '\0';
+ //Box(BOX_OK, "%s", buf_p);
+ //MessageBox(hwnd, buf_p, "License", MB_ICONINFORMATION);
+ warning("printBootText(): License: %s", buf);
+ }
+
+ free(buf);
+ ofp.close();
+}
+
+void FileManager::readBootFile() {
+// Reads boot file for program environment. Fatal error if not there or
+// file checksum is bad. De-crypts structure while checking checksum
+ debugC(1, kDebugFile, "readBootFile");
+
+ Common::File ofp;
+ if (!ofp.open(BOOTFILE)) {
+ if (_vm._gameVariant == 3) {
+ //TODO initialize properly _boot structure
+ warning("readBootFile - Skipping as H1 Dos may be a freeware");
+ return;
+ } else {
+ Utils::Error(FILE_ERR, "%s", BOOTFILE);
+ }
+ }
+
+ if (ofp.size() < (int32)sizeof(_boot))
+ Utils::Error(FILE_ERR, "%s", BOOTFILE);
+
+ _boot.checksum = ofp.readByte();
+ _boot.registered = ofp.readByte();
+ ofp.read(_boot.pbswitch, sizeof(_boot.pbswitch));
+ ofp.read(_boot.distrib, sizeof(_boot.distrib));
+ _boot.exit_len = ofp.readUint16LE();
+
+ byte *p = (byte *)&_boot;
+
+ byte checksum = 0;
+ for (uint32 i = 0; i < sizeof(_boot); i++) {
+ checksum ^= p[i];
+ p[i] ^= CRYPT[i % strlen(CRYPT)];
+ }
+ ofp.close();
+
+ if (checksum)
+ Utils::Error(GEN_ERR, "%s", "Program startup file invalid");
+}
+
+uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
+// Returns address of uif_hdr[id], reading it in if first call
+ debugC(1, kDebugFile, "getUIFHeader(%d)", id);
+
+ static bool firstFl = true;
+ static uif_hdr_t UIFHeader[MAX_UIFS]; // Lookup for uif fonts/images
+
+ // Initialize offset lookup if not read yet
+ if (firstFl) {
+ firstFl = false;
+ // Open unbuffered to do far read
+ Common::File ip; // Image data file
+ if (!ip.open(UIF_FILE))
+ Utils::Error(FILE_ERR, "%s", UIF_FILE);
+
+ if (ip.size() < (int32)sizeof(UIFHeader))
+ Utils::Error(FILE_ERR, "%s", UIF_FILE);
+
+ for (int i = 0; i < MAX_UIFS; ++i) {
+ UIFHeader[i].size = ip.readUint16LE();
+ UIFHeader[i].offset = ip.readUint32LE();
+ }
+
+ ip.close();
+ }
+ return &UIFHeader[id];
+}
+
+void FileManager::readUIFItem(int16 id, byte *buf) {
+// Read uif item into supplied buffer.
+ debugC(1, kDebugFile, "readUIFItem(%d, ...)", id);
+
+ // Open uif file to read data
+ Common::File ip; // UIF_FILE handle
+ if (!ip.open(UIF_FILE))
+ Utils::Error(FILE_ERR, "%s", UIF_FILE);
+
+ // Seek to data
+ uif_hdr_t *UIFHeaderPtr = getUIFHeader((uif_t)id);
+ ip.seek(UIFHeaderPtr->offset, SEEK_SET);
+
+ // We support pcx images and straight data
+ seq_t dummySeq; // Dummy seq_t for image data
+ switch (id) {
+ case UIF_IMAGES: // Read uif images file
+ readPCX(ip, &dummySeq, buf, true, UIF_FILE);
+ break;
+ default: // Read file data into supplied array
+ if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
+ Utils::Error(FILE_ERR, "%s", UIF_FILE);
+ break;
+ }
+
+ ip.close();
+}
+
+void FileManager::instructions() {
+// Simple instructions given when F1 pressed twice in a row
+// Only in DOS versions
+
+ Common::File f;
+ if (!f.open(HELPFILE)) {
+ warning("help.dat not found");
+ return;
+ }
+
+ char readBuf[2];
+ while (f.read(readBuf, 1)) {
+ char line[1024], *wrkLine;
+ wrkLine = line;
+ wrkLine[0] = readBuf[0];
+ wrkLine++;
+ do {
+ f.read(wrkLine, 1);
+ } while (*wrkLine++ != EOP);
+ wrkLine[-2] = '\0'; // Remove EOP and previous CR
+ Utils::Box(BOX_ANY, "%s", line);
+ wrkLine = line;
+ f.read(readBuf, 2); // Remove CRLF after EOP
+ }
+ f.close();
+}
+
+} // End of namespace Hugo
+
diff --git a/engines/hugo/file.h b/engines/hugo/file.h
new file mode 100644
index 0000000000..a5679d0e1b
--- /dev/null
+++ b/engines/hugo/file.h
@@ -0,0 +1,155 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_FILE_H
+#define HUGO_FILE_H
+
+// TODO get rid of those defines
+#define HELPFILE "help.dat"
+#define EOP '#' // Marks end of a page in help file
+
+struct PCC_header_t { // Structure of PCX file header
+ byte mfctr, vers, enc, bpx;
+ uint16 x1, y1, x2, y2; // bounding box
+ uint16 xres, yres;
+ byte palette[48]; // EGA color palette
+ byte vmode, planes;
+ uint16 bytesPerLine; // Bytes per line
+ byte fill2[60];
+}; // Header of a PCC file
+
+// Record and playback handling stuff:
+struct pbdata_t {
+// int key; // Character
+ uint32 time; // Time at which character was pressed
+};
+
+namespace Hugo {
+
+class FileManager {
+public:
+ FileManager(HugoEngine &vm);
+ virtual ~FileManager();
+
+
+ bool fileExists(char *filename);
+ sound_pt getSound(short sound, uint16 *size);
+
+ void closePlaybackFile();
+ void initSavedGame();
+ void instructions();
+ void readBootFile();
+ void readImage(int objNum, object_t *objPtr);
+ void readUIFItem(short id, byte *buf);
+ void restoreGame(short slot);
+ void restoreSeq(object_t *obj);
+ void saveGame(short slot, const char *descrip);
+ void saveSeq(object_t *obj);
+
+ virtual void openDatabaseFiles() = 0;
+ virtual void closeDatabaseFiles() = 0;
+
+ virtual void readBackground(int screenIndex) = 0;
+ virtual void readOverlay(int screenNum, image_pt image, ovl_t overlayType) = 0;
+
+ virtual char *fetchString(int index) = 0;
+
+protected:
+ HugoEngine &_vm;
+
+ Common::File _stringArchive; // Handle for string file
+ Common::File _sceneryArchive1; // Handle for scenery file
+ Common::File _objectsArchive; // Handle for objects file
+
+ seq_t *readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name);
+private:
+
+ byte *convertPCC(byte *p, uint16 y, uint16 bpl, image_pt data_p);
+ uif_hdr_t *getUIFHeader(uif_t id);
+
+ pbdata_t pbdata;
+ FILE *fpb;
+
+//Strangerke : Not used?
+ void openPlaybackFile(bool playbackFl, bool recordFl);
+ void printBootText();
+// bool pkkey();
+// char pbget();
+};
+
+class FileManager_v1d : public FileManager {
+public:
+ FileManager_v1d(HugoEngine &vm);
+ ~FileManager_v1d();
+
+ void openDatabaseFiles();
+ void closeDatabaseFiles();
+ void readBackground(int screenIndex);
+ void readOverlay(int screenNum, image_pt image, ovl_t overlayType);
+ char *fetchString(int index);
+};
+
+class FileManager_v2d : public FileManager {
+public:
+ FileManager_v2d(HugoEngine &vm);
+ ~FileManager_v2d();
+
+ void openDatabaseFiles();
+ void closeDatabaseFiles();
+ void readBackground(int screenIndex);
+ void readOverlay(int screenNum, image_pt image, ovl_t overlayType);
+ char *fetchString(int index);
+};
+
+class FileManager_v3d : public FileManager_v2d {
+public:
+ FileManager_v3d(HugoEngine &vm);
+ ~FileManager_v3d();
+
+ void openDatabaseFiles();
+ void closeDatabaseFiles();
+ void readBackground(int screenIndex);
+ void readOverlay(int screenNum, image_pt image, ovl_t overlayType);
+private:
+ Common::File _sceneryArchive2; // Handle for scenery file
+};
+
+class FileManager_v1w : public FileManager_v2d {
+public:
+ FileManager_v1w(HugoEngine &vm);
+ ~FileManager_v1w();
+
+ void readOverlay(int screenNum, image_pt image, ovl_t overlayType);
+};
+
+} // End of namespace Hugo
+#endif //HUGO_FILE_H
diff --git a/engines/hugo/file_v1d.cpp b/engines/hugo/file_v1d.cpp
new file mode 100644
index 0000000000..b6239aa5dc
--- /dev/null
+++ b/engines/hugo/file_v1d.cpp
@@ -0,0 +1,101 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager_v1d::FileManager_v1d(HugoEngine &vm) : FileManager(vm) {
+}
+
+FileManager_v1d::~FileManager_v1d() {
+}
+
+void FileManager_v1d::openDatabaseFiles() {
+ debugC(1, kDebugFile, "openDatabaseFiles");
+}
+
+void FileManager_v1d::closeDatabaseFiles() {
+ debugC(1, kDebugFile, "closeDatabaseFiles");
+}
+
+void FileManager_v1d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+ debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+ const char *ovl_ext[] = {".b", ".o", ".ob"};
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+
+ strcat(strcpy(buf, _vm._screenNames[screenNum]), ovl_ext[overlayType]);
+
+ if (!fileExists(buf)) {
+ for (uint32 i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+
+ if (!_sceneryArchive1.open(buf))
+ Utils::Error(FILE_ERR, "%s", buf);
+
+ image_pt tmpImage = image; // temp ptr to overlay file
+
+ _sceneryArchive1.read(tmpImage, OVL_SIZE);
+ _sceneryArchive1.close();
+}
+
+void FileManager_v1d::readBackground(int screenIndex) {
+// Read a PCX image into dib_a
+ debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
+
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+ strcat(strcpy(buf, _vm._screenNames[screenIndex]), ".ART");
+ if (!_sceneryArchive1.open(buf))
+ Utils::Error(FILE_ERR, "%s", buf);
+ // Read the image into dummy seq and static dib_a
+ seq_t dummySeq; // Image sequence structure for Read_pcx
+ readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+
+ _sceneryArchive1.close();
+}
+
+char *FileManager_v1d::fetchString(int index) {
+ debugC(1, kDebugFile, "fetchString(%d)", index);
+
+ return _vm._stringtData[index];
+}
+
+} // End of namespace Hugo
+
diff --git a/engines/hugo/file_v1w.cpp b/engines/hugo/file_v1w.cpp
new file mode 100644
index 0000000000..6ab21a853e
--- /dev/null
+++ b/engines/hugo/file_v1w.cpp
@@ -0,0 +1,90 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager_v1w::FileManager_v1w(HugoEngine &vm) : FileManager_v2d(vm) {
+}
+
+FileManager_v1w::~FileManager_v1w() {
+}
+
+void FileManager_v1w::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+ debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+ image_pt tmpImage = image; // temp ptr to overlay file
+ _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock_t sceneBlock; // Database header entry
+ sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
+
+ uint32 i = 0;
+ switch (overlayType) {
+ case BOUNDARY:
+ _sceneryArchive1.seek(sceneBlock.b_off, SEEK_SET);
+ i = sceneBlock.b_len;
+ break;
+ case OVERLAY:
+ _sceneryArchive1.seek(sceneBlock.o_off, SEEK_SET);
+ i = sceneBlock.o_len;
+ break;
+ case OVLBASE:
+ _sceneryArchive1.seek(sceneBlock.ob_off, SEEK_SET);
+ i = sceneBlock.ob_len;
+ break;
+ default:
+ Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ break;
+ }
+ if (i == 0) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+ _sceneryArchive1.read(tmpImage, OVL_SIZE);
+}
+
+} // End of namespace Hugo
+
diff --git a/engines/hugo/file_v2d.cpp b/engines/hugo/file_v2d.cpp
new file mode 100644
index 0000000000..43de3fac4c
--- /dev/null
+++ b/engines/hugo/file_v2d.cpp
@@ -0,0 +1,177 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/global.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager_v2d::FileManager_v2d(HugoEngine &vm) : FileManager(vm) {
+}
+
+FileManager_v2d::~FileManager_v2d() {
+}
+
+void FileManager_v2d::openDatabaseFiles() {
+ debugC(1, kDebugFile, "openDatabaseFiles");
+
+ if (!_stringArchive.open(STRING_FILE))
+ Utils::Error(FILE_ERR, "%s", STRING_FILE);
+ if (!_sceneryArchive1.open("scenery.dat"))
+ Utils::Error(FILE_ERR, "%s", "scenery.dat");
+ if (!_objectsArchive.open(OBJECTS_FILE))
+ Utils::Error(FILE_ERR, "%s", OBJECTS_FILE);
+}
+
+void FileManager_v2d::closeDatabaseFiles() {
+ debugC(1, kDebugFile, "closeDatabaseFiles");
+
+ _stringArchive.close();
+ _sceneryArchive1.close();
+ _objectsArchive.close();
+}
+
+void FileManager_v2d::readBackground(int screenIndex) {
+// Read a PCX image into dib_a
+ debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
+
+ _sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock_t sceneBlock; // Read a database header entry
+ sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
+
+ _sceneryArchive1.seek(sceneBlock.scene_off, SEEK_SET);
+
+ // Read the image into dummy seq and static dib_a
+ seq_t dummySeq; // Image sequence structure for Read_pcx
+ readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+}
+
+void FileManager_v2d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+ debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+ image_pt tmpImage = image; // temp ptr to overlay file
+ _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock_t sceneBlock; // Database header entry
+ sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
+
+ uint32 i = 0;
+ switch (overlayType) {
+ case BOUNDARY:
+ _sceneryArchive1.seek(sceneBlock.b_off, SEEK_SET);
+ i = sceneBlock.b_len;
+ break;
+ case OVERLAY:
+ _sceneryArchive1.seek(sceneBlock.o_off, SEEK_SET);
+ i = sceneBlock.o_len;
+ break;
+ case OVLBASE:
+ _sceneryArchive1.seek(sceneBlock.ob_off, SEEK_SET);
+ i = sceneBlock.ob_len;
+ break;
+ default:
+ Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ break;
+ }
+ if (i == 0) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+
+ // Read in the overlay file using MAC Packbits. (We're not proud!)
+ int16 k = 0; // byte count
+ do {
+ int8 data = _sceneryArchive1.readByte(); // Read a code byte
+ if ((byte)data == 0x80) // Noop
+ k = k;
+ else if (data >= 0) { // Copy next data+1 literally
+ for (i = 0; i <= (byte)data; i++, k++)
+ *tmpImage++ = _sceneryArchive1.readByte();
+ } else { // Repeat next byte -data+1 times
+ int16 j = _sceneryArchive1.readByte();
+
+ for (i = 0; i < (byte)(-data + 1); i++, k++)
+ *tmpImage++ = j;
+ }
+ } while (k < OVL_SIZE);
+}
+
+char *FileManager_v2d::fetchString(int index) {
+// Fetch string from file, decode and return ptr to string in memory
+ debugC(1, kDebugFile, "fetchString(%d)", index);
+
+ // Get offset to string[index] (and next for length calculation)
+ _stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET);
+ uint32 off1, off2;
+ if (_stringArchive.read((char *)&off1, sizeof(uint32)) == 0)
+ Utils::Error(FILE_ERR, "%s", "String offset");
+ if (_stringArchive.read((char *)&off2, sizeof(uint32)) == 0)
+ Utils::Error(FILE_ERR, "%s", "String offset");
+
+ // Check size of string
+ if ((off2 - off1) >= MAX_BOX)
+ Utils::Error(FILE_ERR, "%s", "Fetched string too long!");
+
+ // Position to string and read it into gen purpose _textBoxBuffer
+ _stringArchive.seek(off1, SEEK_SET);
+ if (_stringArchive.read(_textBoxBuffer, (uint16)(off2 - off1)) == 0)
+ Utils::Error(FILE_ERR, "%s", "Fetch_string");
+
+ // Null terminate, decode and return it
+ _textBoxBuffer[off2-off1] = '\0';
+ _vm.scheduler().decodeString(_textBoxBuffer);
+ return _textBoxBuffer;
+}
+} // End of namespace Hugo
+
diff --git a/engines/hugo/file_v3d.cpp b/engines/hugo/file_v3d.cpp
new file mode 100644
index 0000000000..e4809a7208
--- /dev/null
+++ b/engines/hugo/file_v3d.cpp
@@ -0,0 +1,200 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/global.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager_v3d::FileManager_v3d(HugoEngine &vm) : FileManager_v2d(vm) {
+}
+
+FileManager_v3d::~FileManager_v3d() {
+}
+
+void FileManager_v3d::readBackground(int screenIndex) {
+// Read a PCX image into dib_a
+ debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
+
+ _sceneryArchive1.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock_t sceneBlock; // Read a database header entry
+ sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
+
+ seq_t dummySeq; // Image sequence structure for Read_pcx
+ if (screenIndex < 20) {
+ _sceneryArchive1.seek(sceneBlock.scene_off, SEEK_SET);
+ // Read the image into dummy seq and static dib_a
+ readPCX(_sceneryArchive1, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+ } else {
+ _sceneryArchive2.seek(sceneBlock.scene_off, SEEK_SET);
+ // Read the image into dummy seq and static dib_a
+ readPCX(_sceneryArchive2, &dummySeq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+ }
+}
+
+void FileManager_v3d::openDatabaseFiles() {
+ debugC(1, kDebugFile, "openDatabaseFiles");
+
+ if (!_stringArchive.open(STRING_FILE))
+ Utils::Error(FILE_ERR, "%s", STRING_FILE);
+ if (!_sceneryArchive1.open("scenery1.dat"))
+ Utils::Error(FILE_ERR, "%s", "scenery1.dat");
+ if (!_sceneryArchive2.open("scenery2.dat"))
+ Utils::Error(FILE_ERR, "%s", "scenery2.dat");
+ if (!_objectsArchive.open(OBJECTS_FILE))
+ Utils::Error(FILE_ERR, "%s", OBJECTS_FILE);
+}
+
+void FileManager_v3d::closeDatabaseFiles() {
+ debugC(1, kDebugFile, "closeDatabaseFiles");
+
+ _stringArchive.close();
+ _sceneryArchive1.close();
+ _sceneryArchive2.close();
+ _objectsArchive.close();
+}
+
+void FileManager_v3d::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+ debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+ image_pt tmpImage = image; // temp ptr to overlay file
+ _sceneryArchive1.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock_t sceneBlock; // Database header entry
+ sceneBlock.scene_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive1.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive1.readUint32LE();
+
+ uint32 i = 0;
+
+ if (screenNum < 20) {
+ switch (overlayType) {
+ case BOUNDARY:
+ _sceneryArchive1.seek(sceneBlock.b_off, SEEK_SET);
+ i = sceneBlock.b_len;
+ break;
+ case OVERLAY:
+ _sceneryArchive1.seek(sceneBlock.o_off, SEEK_SET);
+ i = sceneBlock.o_len;
+ break;
+ case OVLBASE:
+ _sceneryArchive1.seek(sceneBlock.ob_off, SEEK_SET);
+ i = sceneBlock.ob_len;
+ break;
+ default:
+ Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ break;
+ }
+ if (i == 0) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+
+ // Read in the overlay file using MAC Packbits. (We're not proud!)
+ int16 k = 0; // byte count
+ do {
+ int8 data = _sceneryArchive1.readByte();// Read a code byte
+ if ((byte)data == 0x80) // Noop
+ k = k;
+ else if (data >= 0) { // Copy next data+1 literally
+ for (i = 0; i <= (byte)data; i++, k++)
+ *tmpImage++ = _sceneryArchive1.readByte();
+ } else { // Repeat next byte -data+1 times
+ int16 j = _sceneryArchive1.readByte();
+
+ for (i = 0; i < (byte)(-data + 1); i++, k++)
+ *tmpImage++ = j;
+ }
+ } while (k < OVL_SIZE);
+ } else {
+ switch (overlayType) {
+ case BOUNDARY:
+ _sceneryArchive2.seek(sceneBlock.b_off, SEEK_SET);
+ i = sceneBlock.b_len;
+ break;
+ case OVERLAY:
+ _sceneryArchive2.seek(sceneBlock.o_off, SEEK_SET);
+ i = sceneBlock.o_len;
+ break;
+ case OVLBASE:
+ _sceneryArchive2.seek(sceneBlock.ob_off, SEEK_SET);
+ i = sceneBlock.ob_len;
+ break;
+ default:
+ Utils::Error(FILE_ERR, "%s", "Bad ovl_type");
+ break;
+ }
+ if (i == 0) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+
+ // Read in the overlay file using MAC Packbits. (We're not proud!)
+ int16 k = 0; // byte count
+ do {
+ int8 data = _sceneryArchive2.readByte();// Read a code byte
+ if ((byte)data == 0x80) // Noop
+ k = k;
+ else if (data >= 0) { // Copy next data+1 literally
+ for (i = 0; i <= (byte)data; i++, k++)
+ *tmpImage++ = _sceneryArchive2.readByte();
+ } else { // Repeat next byte -data+1 times
+ int16 j = _sceneryArchive2.readByte();
+
+ for (i = 0; i < (byte)(-data + 1); i++, k++)
+ *tmpImage++ = j;
+ }
+ } while (k < OVL_SIZE);
+ }
+}
+} // End of namespace Hugo
+
diff --git a/engines/hugo/game.h b/engines/hugo/game.h
new file mode 100644
index 0000000000..869aa1baa4
--- /dev/null
+++ b/engines/hugo/game.h
@@ -0,0 +1,872 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_GAME_H
+#define HUGO_GAME_H
+
+#include "common/keyboard.h"
+
+namespace Common {
+class WriteStream;
+class SeekableReadStream;
+}
+
+namespace Hugo {
+
+// WARNING!! Run the program at least once before release to
+// generate the initial save file! (Using the -i cmd switch)
+// Set EPISODE_NUM & build. Build pictures.mak and run "Tools/Hugo N".
+// Copy helpedit\hugow_?.hlp to .\hugowin?.hlp
+// Type "PPG" in the game to enter cheat mode.
+
+#define COPYRIGHT "Copyright 1989-1997 David P Gray, All Rights Reserved."
+// Started code on 04/01/95
+// Don't forget to update Hugowin.rc2 with version info
+//#define VER "1.0" // 10/01/95 Initial Release
+//#define VER "1.1" // 10/06/95 Restore system volume levels on exit
+//#define VER "v1.2"// 10/12/95 Added "background music" checkbox in volume dlg
+//#define VER "v1.3"// 10/23/95 Support game 1 as shareware
+//#define VER "v1.4"// 12/06/95 Faster graphics, logical palette
+//#define VER "v1.5" // 10/07/97 Added order form, new web site
+
+// Game specific equates
+#define MAX_TUNES 16 // Max number of tunes
+#define NORMAL_TPS 9 // Number of ticks (frames) per second
+#define TURBO_TPS 16 // This many in turbo mode
+#define DX 5 // Num pixels moved in x by HERO per step
+#define DY 4 // Num pixels moved in y by HERO per step
+#define XBYTES 40 // number of bytes in a compressed line
+#define XPIX 320 // Width of pcx background file
+#define YPIX 200 // Height of pcx background file
+#define VIEW_DX XPIX // Width of window view
+#define VIEW_DY 184 // Height of window view
+#define INV_DX 32 // Width of an inventory icon
+#define INV_DY 32 // Height of inventory icon
+#define DIBOFF_Y 8 // Offset into dib SrcY (old status line area)
+#define OVL_SIZE (XBYTES * YPIX) // Size of an overlay file
+#define MAX_SEQUENCES 4 // Number of sequences of images in object
+#define MAX_CHARS (XBYTES - 2) // Max length of user input line
+#define NUM_ROWS 25 // Number of text lines in display
+#define MAX_BOX (MAX_CHARS * NUM_ROWS) // Max chars on screen
+#define DONT_CARE 0xFF // Any state allowed in command verb
+#define MAX_FPATH 256 // Max length of a full path name
+#define HERO_MAX_WIDTH 24 // Maximum width of hero
+#define HERO_MIN_WIDTH 16 // Minimum width of hero
+#define LOOK_NAME 1 // Index of name used in showing takeables
+#define TAKE_NAME 2 // Index of name used in confirming take
+#define TAKE_TEXT "Picked up the %s ok."
+#define REP_MASK 0xC0 // Top 2 bits mean a repeat code
+#define MAX_STRLEN 1024
+#define WARNLEN 512
+#define ERRLEN 512
+#define STEP_DY 8 // Pixels per step movement
+#define CENTER -1 // Used to center text in x
+
+// Only for non-database
+#define BKGEXT ".PCX" // Extension of background files
+#define OBJEXT ".PIX" // Extension of object picture files
+#define NAME_LEN 12 // Max length of a DOS file name
+
+// Definitions of 'generic' commands: Max # depends on size of gencmd in
+// the object_t record since each requires 1 bit. Currently up to 16
+#define LOOK 1
+#define TAKE 2
+#define DROP 4
+#define LOOK_S 8 // Description depends on state of object
+
+// Macros:
+#define TPS ((_config.turboFl) ? TURBO_TPS : NORMAL_TPS)
+
+#define MAX_UIFS 32 // Max possible uif items in hdr
+#define NUM_FONTS 3 // Number of dib fonts
+#define FIRST_FONT U_FONT5
+#define FONT_LEN 128 // Number of chars in font
+#define FONTSIZE 1200 // Max size of font data
+
+#define CRYPT "Copyright 1992, David P Gray, Gray Design Associates"
+
+enum TEXTCOLORS {
+ _TBLACK, _TBLUE, _TGREEN, _TCYAN,
+ _TRED, _TMAGENTA, _TBROWN, _TWHITE,
+ _TGRAY, _TLIGHTBLUE, _TLIGHTGREEN, _TLIGHTCYAN,
+ _TLIGHTRED, _TLIGHTMAGENTA, _TLIGHTYELLOW, _TBRIGHTWHITE
+};
+
+enum uif_t {U_FONT5, U_FONT6, U_FONT8, UIF_IMAGES, NUM_UIF_ITEMS};
+
+// Enumerate overlay file types
+enum ovl_t {BOUNDARY, OVERLAY, OVLBASE};
+
+// Enumerate error types
+enum {
+ GEN_ERR, FILE_ERR, WRITE_ERR, PCCH_ERR, HEAP_ERR, EVNT_ERR, SOUND_ERR
+ //MOUSE_ERR, VID_ERR, FONT_ERR, ARG_ERR, CHK_ERR, TIMER_ERR, VBX_ERR
+};
+
+// Enumerate ways of cycling a sequence of frames
+enum cycle_t {INVISIBLE, ALMOST_INVISIBLE, NOT_CYCLING, CYCLE_FORWARD, CYCLE_BACKWARD};
+
+// Enumerate sequence index matching direction of travel
+enum {RIGHT, LEFT, DOWN, _UP};
+
+// Channel requirement during sound effect
+enum stereo_t {BOTH_CHANNELS, RIGHT_CHANNEL, LEFT_CHANNEL};
+
+// Priority for sound effect
+enum priority_t {LOW_PRI, MED_PRI, HIGH_PRI};
+
+// Enumerate the different path types for an object
+enum path_t {
+ USER, // User has control of object via cursor keys
+ AUTO, // Computer has control, controlled by action lists
+ QUIET, // Computer has control and no commands allowed
+ CHASE, // Computer has control, object is chasing hero
+ CHASE2, // Same as CHASE, except keeps cycling when stationary
+ WANDER, // Computer has control, object is wandering randomly
+ WANDER2 // Same as WANDER, except keeps cycling when stationary
+};
+
+// Enumerate whether object is foreground, background or 'floating'
+// If floating, HERO can collide with it and fore/back ground is determined
+// by relative y-coord of object base. This is the general case.
+// If fore or background, no collisions can take place and object is either
+// behind or in front of all others, although can still be hidden by the
+// the overlay plane. OVEROVL means the object is FLOATING (to other
+// objects) but is never hidden by the overlay plane
+enum {FOREGROUND, BACKGROUND, FLOATING, OVEROVL};
+
+// Game view state machine
+enum vstate_t {V_IDLE, V_INTROINIT, V_INTRO, V_PLAY, V_INVENT, V_EXIT};
+
+enum font_t {LARGE_ROMAN, MED_ROMAN, NUM_GDI_FONTS, INIT_FONTS, DEL_FONTS};
+
+// Ways to dismiss a text/prompt box
+enum box_t {BOX_ANY, BOX_OK, BOX_PROMPT, BOX_YESNO};
+
+// Standard viewport sizes
+enum wsize_t {SIZE_DEF, SIZE_1, SIZE_2, SIZE_3};
+
+// Display list functions
+enum dupdate_t {D_INIT, D_ADD, D_DISPLAY, D_RESTORE};
+
+// General device installation commands
+enum inst_t {INSTALL, RESTORE, RESET};
+
+// Inventory icon bar states
+enum istate_t {I_OFF, I_UP, I_DOWN, I_ACTIVE};
+
+// Actions for Process_inventory()
+enum invact_t {INV_INIT, INV_LEFT, INV_RIGHT, INV_GET};
+
+// Purpose of an automatic route
+enum go_t {GO_SPACE, GO_EXIT, GO_LOOK, GO_GET};
+
+// Following defines the action types and action list
+enum action_t { // Parameters:
+ ANULL = 0xff, // Special NOP used to 'delete' events in DEL_EVENTS
+ ASCHEDULE = 0, // 0 - Ptr to action list to be rescheduled
+ START_OBJ, // 1 - Object number
+ INIT_OBJXY, // 2 - Object number, x,y
+ PROMPT, // 3 - index of prompt & response string, ptrs to action
+ // lists. First if response matches, 2nd if not.
+ BKGD_COLOR, // 4 - new background color
+ INIT_OBJVXY, // 5 - Object number, vx, vy
+ INIT_CARRY, // 6 - Object number, carried status
+ INIT_HF_COORD, // 7 - Object number (gets hero's 'feet' coordinates)
+ NEW_SCREEN, // 8 - New screen number
+ INIT_OBJSTATE, // 9 - Object number, new object state
+ INIT_PATH, // 10 - Object number, new path type
+ COND_R, // 11 - Conditional on object state - req state, 2 act_lists
+ TEXT, // 12 - Simple text box
+ SWAP_IMAGES, // 13 - Swap 2 object images
+ COND_SCR, // 14 - Conditional on current screen
+ AUTOPILOT, // 15 - Set object to home in on another (stationary) object
+ INIT_OBJ_SEQ, // 16 - Object number, sequence index to set curr_seq_p to
+ SET_STATE_BITS, // 17 - Objnum, mask to OR with obj states word
+ CLEAR_STATE_BITS, // 18 - Objnum, mask to ~AND with obj states word
+ TEST_STATE_BITS, // 19 - Objnum, mask to test obj states word
+ DEL_EVENTS, // 20 - Action type to delete all occurrences of
+ GAMEOVER, // 21 - Disable hero & commands. Game is over
+ INIT_HH_COORD, // 22 - Object number (gets hero's actual coordinates)
+ EXIT, // 23 - Exit game back to DOS
+ BONUS, // 24 - Get score bonus for an action
+ COND_BOX, // 25 - Conditional on object within bounding box
+ SOUND, // 26 - Set currently playing sound
+ ADD_SCORE, // 27 - Add object's value to current score
+ SUB_SCORE, // 28 - Subtract object's value from current score
+ COND_CARRY, // 29 - Conditional on carrying object
+ INIT_MAZE, // 30 - Start special maze hotspot processing
+ EXIT_MAZE, // 31 - Exit special maze processing
+ INIT_PRIORITY, // 32 - Initialize fbg field
+ INIT_SCREEN, // 33 - Initialise screen field of object
+ AGSCHEDULE, // 34 - Global schedule - lasts over new screen
+ REMAPPAL, // 35 - Remappe palette - palette index, color
+ COND_NOUN, // 36 - Conditional on noun appearing in line
+ SCREEN_STATE, // 37 - Set new screen state - used for comments
+ INIT_LIPS, // 38 - Position lips object for supplied object
+ INIT_STORY_MODE, // 39 - Set story mode TRUE/FALSE (user can't type)
+ WARN, // 40 - Same as TEXT but can't dismiss box by typing
+ COND_BONUS, // 41 - Conditional on bonus having been scored
+ TEXT_TAKE, // 42 - Issue text box with "take" info string
+ YESNO, // 43 - Prompt user for Yes or No
+ STOP_ROUTE, // 44 - Skip any route in progress (hero still walks)
+ COND_ROUTE, // 45 - Conditional on route in progress
+ INIT_JUMPEXIT, // 46 - Initialize status.jumpexit
+ INIT_VIEW, // 47 - Initialize viewx, viewy, dir
+ INIT_OBJ_FRAME, // 48 - Object number, seq,frame to set curr_seq_p to
+ OLD_SONG = 49 // Added by Strangerke - Set currently playing sound, old way: that is, using a string index instead of a reference in a file
+};
+
+struct hugo_boot_t { // Common HUGO boot file
+ char checksum; // Checksum for boot structure (not exit text)
+ char registered; // TRUE if registered version, else FALSE
+ char pbswitch[8]; // Playback switch string
+ char distrib[32]; // Distributor branding string
+ uint16 exit_len; // Length of exit text (next in file)
+};
+
+struct uif_hdr_t { // UIF font/image look up
+ uint16 size; // Size of uif item
+ uint32 offset; // Offset of item in file
+};
+
+// Game specific type definitions
+typedef byte *image_pt; // ptr to an object image (sprite)
+typedef byte *sound_pt; // ptr to sound (or music) data
+
+// Following are points for achieving certain actions.
+struct point_t {
+ byte score; // The value of the point
+ bool scoredFl; // Whether scored yet
+};
+
+// Structure for initializing maze processing
+struct maze_t {
+ bool enabledFl; // TRUE when maze processing enabled
+ byte size; // Size of (square) maze matrix
+ int x1, y1, x2, y2; // maze hotspot bounding box
+ int x3, x4; // north, south x entry coordinates
+ byte firstScreenIndex; // index of first screen in maze
+};
+
+struct act0 { // Type 0 - Schedule
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ uint16 actIndex; // Ptr to an action list
+};
+
+struct act1 { // Type 1 - Start an object
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int cycleNumb; // Number of times to cycle
+ cycle_t cycle; // Direction to start cycling
+};
+
+struct act2 { // Type 2 - Initialise an object coords
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int x, y; // Coordinates
+};
+
+struct act3 { // Type 3 - Prompt user for text
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ uint16 promptIndex; // Index of prompt string
+ int *responsePtr; // Array of indexes to valid response
+ // string(s) (terminate list with -1)
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+ bool encodedFl; // (HUGO 1 DOS ONLY) Whether response is encoded or not
+};
+
+struct act4 { // Type 4 - Set new background color
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ long newBackgroundColor; // New color
+};
+
+struct act5 { // Type 5 - Initialise an object velocity
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int vx, vy; // velocity
+};
+
+struct act6 { // Type 6 - Initialise an object carrying
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ bool carriedFl; // carrying
+};
+
+struct act7 { // Type 7 - Initialise an object to hero's coords
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+};
+
+struct act8 { // Type 8 - switch to new screen
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int screenIndex; // The new screen number
+};
+
+struct act9 { // Type 9 - Initialise an object state
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ byte newState; // New state
+};
+
+struct act10 { // Type 10 - Initialise an object path type
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int newPathType; // New path type
+ int8 vxPath, vyPath; // Max delta velocities e.g. for CHASE
+};
+
+struct act11 { // Type 11 - Conditional on object's state
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ byte stateReq; // Required state
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+};
+
+struct act12 { // Type 12 - Simple text box
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int stringIndex; // Index (enum) of string in strings.dat
+};
+
+struct act13 { // Type 13 - Swap first object image with second
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int obj1; // Index of first object
+ int obj2; // 2nd
+};
+
+struct act14 { // Type 14 - Conditional on current screen
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The required object
+ int screenReq; // The required screen number
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+};
+
+struct act15 { // Type 15 - Home in on an object
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int obj1; // The object number homing in
+ int obj2; // The object number to home in on
+ int8 dx, dy; // Max delta velocities
+};
+// Note: Don't set a sequence at time 0 of a new screen, it causes
+// problems clearing the boundary bits of the object! timer > 0 is safe
+struct act16 { // Type 16 - Set curr_seq_p to seq
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int seqIndex; // The index of seq array to set to
+};
+
+struct act17 { // Type 17 - SET obj individual state bits
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int stateMask; // The mask to OR with current obj state
+};
+
+struct act18 { // Type 18 - CLEAR obj individual state bits
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int stateMask; // The mask to ~AND with current obj state
+};
+
+struct act19 { // Type 19 - TEST obj individual state bits
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int stateMask; // The mask to AND with current obj state
+ uint16 actPassIndex; // Ptr to action list (all bits set)
+ uint16 actFailIndex; // Ptr to action list (not all set)
+};
+
+struct act20 { // Type 20 - Remove all events with this type of action
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ action_t actTypeDel; // The action type to remove
+};
+
+struct act21 { // Type 21 - Gameover. Disable hero & commands
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+};
+
+struct act22 { // Type 22 - Initialise an object to hero's coords
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+};
+
+struct act23 { // Type 23 - Exit game back to DOS
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+};
+
+struct act24 { // Type 24 - Get bonus score
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int pointIndex; // Index into points array
+};
+
+struct act25 { // Type 25 - Conditional on bounding box
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The required object number
+ int x1, y1, x2, y2; // The bounding box
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+};
+
+struct act26 { // Type 26 - Play a sound
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int16 soundIndex; // Sound index in data file
+};
+
+struct act27 { // Type 27 - Add object's value to score
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // object number
+};
+
+struct act28 { // Type 28 - Subtract object's value from score
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // object number
+};
+
+struct act29 { // Type 29 - Conditional on object carried
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The required object number
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+};
+
+struct act30 { // Type 30 - Start special maze processing
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ byte mazeSize; // Size of (square) maze
+ int x1, y1, x2, y2; // Bounding box of maze
+ int x3, x4; // Extra x points for perspective correction
+ byte firstScreenIndex; // First (top left) screen of maze
+};
+
+struct act31 { // Type 31 - Exit special maze processing
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+};
+
+struct act32 { // Type 32 - Init fbg field of object
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ byte priority; // Value of foreground/background field
+};
+
+struct act33 { // Type 33 - Init screen field of object
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int screenIndex; // Screen number
+};
+
+struct act34 { // Type 34 - Global Schedule
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ uint16 actIndex; // Ptr to an action list
+};
+
+struct act35 { // Type 35 - Remappe palette
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int16 oldColorIndex; // Old color index, 0..15
+ int16 newColorIndex; // New color index, 0..15
+};
+
+struct act36 { // Type 36 - Conditional on noun mentioned
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ uint16 nounIndex; // The required noun (list)
+ uint16 actPassIndex; // Ptr to action list if success
+ uint16 actFailIndex; // Ptr to action list if failure
+};
+
+struct act37 { // Type 37 - Set new screen state
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int screenIndex; // The screen number
+ byte newState; // The new state
+};
+
+struct act38 { // Type 38 - Position lips
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int lipsObjNumb; // The LIPS object
+ int objNumb; // The object to speak
+ byte dxLips; // Relative offset of x
+ byte dyLips; // Relative offset of y
+};
+
+struct act39 { // Type 39 - Init story mode
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ bool storyModeFl; // New state of story_mode flag
+};
+
+struct act40 { // Type 40 - Unsolicited text box
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int stringIndex; // Index (enum) of string in strings.dat
+};
+
+struct act41 { // Type 41 - Conditional on bonus scored
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int BonusIndex; // Index into bonus list
+ uint16 actPassIndex; // Index of the action list if scored for the first time
+ uint16 actFailIndex; // Index of the action list if already scored
+};
+
+struct act42 { // Type 42 - Text box with "take" string
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object taken
+};
+
+struct act43 { // Type 43 - Prompt user for Yes or No
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int promptIndex; // index of prompt string
+ uint16 actYesIndex; // Ptr to action list if YES
+ uint16 actNoIndex; // Ptr to action list if NO
+};
+
+struct act44 { // Type 44 - Stop any route in progress
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+};
+
+struct act45 { // Type 45 - Conditional on route in progress
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int routeIndex; // Must be >= current status.rindex
+ uint16 actPassIndex; // Ptr to action list if en-route
+ uint16 actFailIndex; // Ptr to action list if not
+};
+
+struct act46 { // Type 46 - Init status.jumpexit
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ bool jumpExitFl; // New state of jumpexit flag
+};
+
+struct act47 { // Type 47 - Init viewx,viewy,dir
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object
+ int16 viewx; // object.viewx
+ int16 viewy; // object.viewy
+ int16 direction; // object.dir
+};
+
+struct act48 { // Type 48 - Set curr_seq_p to frame n
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ int objNumb; // The object number
+ int seqIndex; // The index of seq array to set to
+ int frameIndex; // The index of frame to set to
+};
+
+struct act49 { // Added by Strangerke - Type 79 - Play a sound (DOS way)
+ action_t actType; // The type of action
+ int timer; // Time to set off the action
+ uint16 soundIndex; // Sound index in string array
+};
+
+union act {
+ act0 a0;
+ act1 a1;
+ act2 a2;
+ act3 a3;
+ act4 a4;
+ act5 a5;
+ act6 a6;
+ act7 a7;
+ act8 a8;
+ act9 a9;
+ act10 a10;
+ act11 a11;
+ act12 a12;
+ act13 a13;
+ act14 a14;
+ act15 a15;
+ act16 a16;
+ act17 a17;
+ act18 a18;
+ act19 a19;
+ act20 a20;
+ act21 a21;
+ act22 a22;
+ act23 a23;
+ act24 a24;
+ act25 a25;
+ act26 a26;
+ act27 a27;
+ act28 a28;
+ act29 a29;
+ act30 a30;
+ act31 a31;
+ act32 a32;
+ act33 a33;
+ act34 a34;
+ act35 a35;
+ act36 a36;
+ act37 a37;
+ act38 a38;
+ act39 a39;
+ act40 a40;
+ act41 a41;
+ act42 a42;
+ act43 a43;
+ act44 a44;
+ act45 a45;
+ act46 a46;
+ act47 a47;
+ act48 a48;
+ act49 a49;
+};
+
+// The following determines how a verb is acted on, for an object
+struct cmd {
+ uint16 verbIndex; // the verb
+ uint16 reqIndex; // ptr to list of required objects
+ uint16 textDataNoCarryIndex; // ptr to string if any of above not carried
+ byte reqState; // required state for verb to be done
+ byte newState; // new states if verb done
+ uint16 textDataWrongIndex; // ptr to string if wrong state
+ uint16 textDataDoneIndex; // ptr to string if verb done
+ uint16 actIndex; // Ptr to action list if verb done
+};
+
+// The following is a linked list of images in an animation sequence
+// The image data is in 8-bit DIB format, i.e. 1 byte = 1 pixel
+struct seq_t { // Linked list of images
+ byte *imagePtr; // ptr to image
+ uint16 bytesPerLine8; // bytes per line (8bits)
+ uint16 lines; // lines
+ uint16 x1, x2, y1, y2; // Offsets from x,y: data bounding box
+ seq_t *nextSeqPtr; // ptr to next record
+};
+
+// The following is an array of structures of above sequences
+struct seqList_t {
+ uint16 imageNbr; // Number of images in sequence
+ seq_t *seqPtr; // Ptr to sequence structure
+};
+
+// Following is definition of object attributes
+struct object_t {
+ uint16 nounIndex; // String identifying object
+ uint16 dataIndex; // String describing the object
+ uint16 *stateDataIndex; // Added by Strangerke to handle the LOOK_S state-dependant descriptions
+ path_t pathType; // Describe path object follows
+ int vxPath, vyPath; // Delta velocities (e.g. for CHASE)
+ uint16 actIndex; // Action list to do on collision with hero
+ byte seqNumb; // Number of sequences in list
+ seq_t *currImagePtr; // Sequence image currently in use
+ seqList_t seqList[MAX_SEQUENCES]; // Array of sequence structure ptrs and lengths
+ cycle_t cycling; // Whether cycling, forward or backward
+ byte cycleNumb; // No. of times to cycle
+ byte frameInterval; // Interval (in ticks) between frames
+ byte frameTimer; // Decrementing timer for above
+ int8 radius; // Defines sphere of influence by hero
+ byte screenIndex; // Screen in which object resides
+ int x, y; // Current coordinates of object
+ int oldx, oldy; // Previous coordinates of object
+ int8 vx, vy; // Velocity
+ byte objValue; // Value of object
+ int genericCmd; // Bit mask of 'generic' commands for object
+ uint16 cmdIndex; // ptr to list of cmd structures for verbs
+ bool carriedFl; // TRUE if object being carried
+ byte state; // state referenced in cmd list
+ bool verbOnlyFl; // TRUE if verb-only cmds allowed e.g. sit,look
+ byte priority; // Whether object fore, background or floating
+ int16 viewx, viewy; // Position to view object from (or 0 or -1)
+ int16 direction; // Direction to view object from
+ byte curSeqNum; // Save which seq number currently in use
+ byte curImageNum; // Save which image of sequence currently in use
+ int8 oldvx; // Previous vx (used in wandering)
+ int8 oldvy; // Previous vy
+};
+
+// Following is structure of verbs and nouns for 'background' objects
+// These are objects that appear in the various screens, but nothing
+// interesting ever happens with them. Rather than just be dumb and say
+// "don't understand" we produce an interesting msg to keep user sane.
+struct background_t {
+ uint16 verbIndex;
+ uint16 nounIndex;
+ int commentIndex; // Index of comment produced on match
+ bool matchFl; // TRUE if noun must match when present
+ byte roomState; // "State" of room. Comments might differ.
+ byte bonusIndex; // Index of bonus score (0 = no bonus)
+};
+
+typedef background_t *objectList_t;
+
+typedef byte overlay_t[OVL_SIZE]; // Overlay file
+typedef byte viewdib_t[(long)XPIX *YPIX]; // Viewport dib
+typedef byte icondib_t[XPIX *INV_DY]; // Icon bar dib
+
+typedef char command_t[MAX_CHARS + 8]; // Command line (+spare for prompt,cursor)
+typedef char fpath_t[MAX_FPATH]; // File path
+
+// Structure to define an EXIT or other collision-activated hotspot
+struct hotspot_t {
+ int screenIndex; // Screen in which hotspot appears
+ int x1, y1, x2, y2; // Bounding box of hotspot
+ uint16 actIndex; // Actions to carry out if a 'hit'
+ int16 viewx, viewy, direction; // Used in auto-route mode
+};
+
+struct status_t { // Game status (not saved)
+ bool initSaveFl; // Force save of initial game
+ bool storyModeFl; // Game is telling story - no commands
+ bool gameOverFl; // Game is over - hero knobbled
+ bool playbackFl; // Game is in playback mode
+ bool recordFl; // Game is in record mode
+ bool demoFl; // Game is in demo mode
+ bool debugFl; // Game is in debug mode
+ bool textBoxFl; // Game is (halted) in text box
+// Strangerke - Not used ?
+// bool mmtimeFl; // Multimedia timer supported
+ bool lookFl; // Toolbar "look" button pressed
+ bool recallFl; // Toolbar "recall" button pressed
+ bool leftButtonFl; // Left mouse button pressed
+ bool rightButtonFl; // Right button pressed
+ bool newScreenFl; // New screen just loaded in dib_a
+ bool jumpExitFl; // Allowed to jump to a screen exit
+ bool godModeFl; // Allow DEBUG features in live version
+ bool helpFl; // Calling WinHelp (don't disable music)
+ bool doQuitFl;
+ uint32 tick; // Current time in ticks
+ uint32 saveTick; // Time of last save in ticks
+ vstate_t viewState; // View state machine
+ istate_t inventoryState; // Inventory icon bar state
+ int16 inventoryHeight; // Inventory icon bar height
+ int16 inventoryObjId; // Inventory object selected, or -1
+ int16 routeIndex; // Index into route list, or -1
+ go_t go_for; // Purpose of an automatic route
+ int16 go_id; // Index of exit of object walking to
+ fpath_t path; // Alternate path for saved files
+ long saveSize; // Size of a saved game
+ int16 saveSlot; // Current slot to save/restore game
+ int16 screenWidth; // Desktop screen width
+ int16 song; // Current song
+ int16 cx, cy; // Cursor position (dib coords)
+};
+
+struct config_t { // User's config (saved)
+ bool musicFl; // State of Music button/menu item
+ bool soundFl; // State of Sound button/menu item
+ bool turboFl; // State of Turbo button/menu item
+ bool backgroundMusicFl; // Continue music when task inactive
+ byte musicVolume; // Music volume percentage
+ byte soundVolume; // Sound volume percentage
+ bool playlist[MAX_TUNES]; // Tune playlist
+};
+
+struct target_t { // Secondary target for action
+ uint16 nounIndex; // Secondary object
+ uint16 verbIndex; // Action on secondary object
+};
+
+struct uses_t { // Define uses of certain objects
+ int16 objId; // Primary object
+ uint16 dataIndex; // String if no secondary object matches
+ target_t *targets; // List of secondary targets
+};
+
+// Global externs
+extern config_t _config; // User's config
+extern maze_t _maze; // Maze control structure
+extern hugo_boot_t _boot; // Boot info structure
+extern char _textBoxBuffer[]; // Useful box text buffer
+extern command_t _line; // Line of user text input
+
+// Structure of scenery file lookup entry
+struct sceneBlock_t {
+ uint32 scene_off;
+ uint32 scene_len;
+ uint32 b_off;
+ uint32 b_len;
+ uint32 o_off;
+ uint32 o_len;
+ uint32 ob_off;
+ uint32 ob_len;
+};
+
+// Structure of object file lookup entry
+struct objBlock_t {
+ uint32 objOffset;
+ uint32 objLength;
+};
+
+#include "common/pack-start.h" // START STRUCT PACKING
+struct sound_hdr_t { // Sound file lookup entry
+ uint16 size; // Size of sound data in bytes
+ uint32 offset; // Offset of sound data in file
+} PACKED_STRUCT;
+#include "common/pack-end.h" // END STRUCT PACKING
+
+} // End of namespace Hugo
+
+#endif
diff --git a/engines/hugo/global.h b/engines/hugo/global.h
new file mode 100644
index 0000000000..0ab21ddb9c
--- /dev/null
+++ b/engines/hugo/global.h
@@ -0,0 +1,54 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+namespace Hugo {
+
+#define HERO 0 // In all enums, HERO is the first element
+
+#define DESCRIPLEN 32 // Length of description string
+#define MAX_SOUNDS 64 // Max number of sounds
+#define BOOTFILE "HUGO.BSF" // Name of boot structure file
+#define LEN_MASK 0x3F // Lower 6 bits are length
+#define PBFILE "playback.dat"
+
+// Name scenery and objects picture databases
+#define OBJECTS_FILE "objects.dat"
+#define STRING_FILE "strings.dat"
+#define SOUND_FILE "sounds.dat"
+
+// User interface database (Windows Only)
+// This file contains, between others, the bitmaps of the fonts used in the application
+#define UIF_FILE "uif.dat"
+
+static const int kSavegameVersion = 1;
+
+} // End of namespace Hugo
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
new file mode 100644
index 0000000000..cdc74b5ae5
--- /dev/null
+++ b/engines/hugo/hugo.cpp
@@ -0,0 +1,1531 @@
+/* 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/events.h"
+#include "common/debug-channels.h"
+
+#include "hugo/hugo.h"
+#include "hugo/global.h"
+#include "hugo/game.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/mouse.h"
+#include "hugo/inventory.h"
+#include "hugo/parser.h"
+#include "hugo/route.h"
+#include "hugo/sound.h"
+#include "hugo/intro.h"
+
+#include "engines/util.h"
+
+namespace Hugo {
+
+HugoEngine *HugoEngine::s_Engine = 0;
+
+overlay_t HugoEngine::_boundary;
+overlay_t HugoEngine::_overlay;
+overlay_t HugoEngine::_ovlBase;
+overlay_t HugoEngine::_objBound;
+
+HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(syst), _gameDescription(gd), _mouseX(0), _mouseY(0),
+ _textData(0), _stringtData(0), _screenNames(0), _textEngine(0), _textIntro(0), _textMouse(0), _textParser(0), _textSchedule(0), _textUtil(0),
+ _arrayNouns(0), _arrayVerbs(0), _arrayReqs(0), _hotspots(0), _invent(0), _uses(0), _catchallList(0), _backgroundObjects(0),
+ _points(0), _cmdList(0), _screenActs(0), _objects(0), _actListArr(0), _heroImage(0), _defltTunes(0), _palette(0), _introX(0),
+ _introY(0), _maxInvent(0), _numBonuses(0), _numScreens(0), _tunesNbr(0), _soundSilence(0), _soundTest(0), _screenStates(0), _numObj(0),
+ _score(0), _maxscore(0)
+
+{
+ DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level");
+ DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
+ DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level");
+ DebugMan.addDebugChannel(kDebugMouse, "Mouse", "Mouse debug level");
+ DebugMan.addDebugChannel(kDebugParser, "Parser", "Parser debug level");
+ DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level");
+ DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level");
+ DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level");
+
+ for (int j = 0; j < NUM_FONTS; j++)
+ _arrayFont[j] = 0;
+}
+
+HugoEngine::~HugoEngine() {
+ delete _soundHandler;
+ delete _route;
+ delete _parser;
+ delete _inventoryHandler;
+ delete _mouseHandler;
+ delete _screen;
+ delete _scheduler;
+ delete _fileManager;
+
+ free(_palette);
+ free(_introX);
+ free(_introY);
+
+#if 0
+ freeTexts(_textData);
+ freeTexts(_stringtData);
+ freeTexts(_textEngine);
+ freeTexts(_textIntro);
+ freeTexts(_textMouse);
+ freeTexts(_textParser);
+ freeTexts(_textSchedule);
+ freeTexts(_textUtil);
+#endif
+ free(_textData);
+ free(_stringtData);
+ free(_screenNames);
+ free(_textEngine);
+ free(_textIntro);
+ free(_textMouse);
+ free(_textParser);
+ free(_textSchedule);
+ free(_textUtil);
+
+ warning("Missing: free _arrayNouns");
+ warning("Missing: free _arrayVerbs");
+
+ free(_arrayReqs);
+ free(_hotspots);
+ free(_invent);
+ free(_uses);
+ free(_catchallList);
+
+ warning("Missing: free _background_objects");
+
+ free(_points);
+
+ warning("Missing: free _cmdList");
+ warning("Missing: free _screenActs");
+ warning("Missing: free _objects");
+
+ free(_defltTunes);
+ free(_screenStates);
+
+ if (_arrayFont[0])
+ free(_arrayFont[0]);
+
+ if (_arrayFont[1])
+ free(_arrayFont[1]);
+
+ if (_arrayFont[2])
+ free(_arrayFont[2]);
+}
+
+GameType HugoEngine::getGameType() const {
+ return _gameType;
+}
+
+Common::Platform HugoEngine::getPlatform() const {
+ return _platform;
+}
+
+bool HugoEngine::isPacked() const {
+ return _packedFl;
+}
+
+Common::Error HugoEngine::run() {
+ s_Engine = this;
+ initGraphics(320, 200, false);
+
+ _mouseHandler = new MouseHandler(*this);
+ _inventoryHandler = new InventoryHandler(*this);
+ _route = new Route(*this);
+ _soundHandler = new SoundHandler(*this);
+
+ switch (_gameVariant) {
+ case 0: // H1 Win
+ _fileManager = new FileManager_v1w(*this);
+ _scheduler = new Scheduler_v3d(*this);
+ _introHandler = new intro_v1w(*this);
+ _screen = new Screen_v1w(*this);
+ _parser = new Parser_v1w(*this);
+ break;
+ case 1:
+ _fileManager = new FileManager_v2d(*this);
+ _scheduler = new Scheduler_v3d(*this);
+ _introHandler = new intro_v2w(*this);
+ _screen = new Screen_v1w(*this);
+ _parser = new Parser_v1w(*this);
+ break;
+ case 2:
+ _fileManager = new FileManager_v2d(*this);
+ _scheduler = new Scheduler_v3d(*this);
+ _introHandler = new intro_v3w(*this);
+ _screen = new Screen_v1w(*this);
+ _parser = new Parser_v1w(*this);
+ break;
+ case 3: // H1 DOS
+ _fileManager = new FileManager_v1d(*this);
+ _scheduler = new Scheduler_v1d(*this);
+ _introHandler = new intro_v1d(*this);
+ _screen = new Screen_v1d(*this);
+ _parser = new Parser_v1d(*this);
+ break;
+ case 4:
+ _fileManager = new FileManager_v2d(*this);
+ _scheduler = new Scheduler_v1d(*this);
+ _introHandler = new intro_v2d(*this);
+ _screen = new Screen_v1d(*this);
+ _parser = new Parser_v2d(*this);
+ break;
+ case 5:
+ _fileManager = new FileManager_v3d(*this);
+ _scheduler = new Scheduler_v3d(*this);
+ _introHandler = new intro_v3d(*this);
+ _screen = new Screen_v1d(*this);
+ _parser = new Parser_v3d(*this);
+ break;
+ }
+
+ if (!loadHugoDat())
+ return Common::kUnknownError;
+
+ // Interesting situation: We have no cursor to show, since
+ // the DOS version had none, and the Windows version just used
+ // the windows default one. Meaning this call will just use whatever
+ // was used last, i.e. the launcher GUI cursor. What to do?
+ g_system->showMouse(true);
+
+ initStatus(); // Initialize game status
+ initConfig(INSTALL); // Initialize user's config
+ initialize();
+ initConfig(RESET); // Reset user's config
+
+ file().restoreGame(-1);
+
+ initMachine();
+
+ // Start the state machine
+ _status.viewState = V_INTROINIT;
+
+ _status.doQuitFl = false;
+
+ while (!_status.doQuitFl) {
+ g_system->updateScreen();
+
+ runMachine();
+ // Handle input
+ Common::Event event;
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ parser().keyHandler(event.kbd.keycode, 0);
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ _status.leftButtonFl = true;
+ break;
+ case Common::EVENT_LBUTTONUP:
+ _status.leftButtonFl = false;
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ _status.rightButtonFl = true;
+ break;
+ case Common::EVENT_RBUTTONUP:
+ _status.rightButtonFl = false;
+ break;
+ case Common::EVENT_QUIT:
+ _status.doQuitFl = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return Common::kNoError;
+}
+
+void HugoEngine::initMachine() {
+ if (_gameVariant == kGameVariantH1Dos)
+ readScreenFiles(0);
+ else
+ file().readBackground(_numScreens - 1); // Splash screen
+ readObjectImages(); // Read all object images
+ if (_platform == Common::kPlatformWindows)
+ readUIFImages(); // Read all uif images (only in Win versions)
+}
+
+void HugoEngine::runMachine() {
+// Hugo game state machine - called during onIdle
+ static uint32 lastTime;
+
+ status_t &gameStatus = getGameStatus();
+ // Don't process if we're in a textbox
+ if (gameStatus.textBoxFl)
+ return;
+
+ // Don't process if gameover
+ if (gameStatus.gameOverFl)
+ return;
+
+ // Process machine once every tick
+ if (g_system->getMillis() - lastTime < (uint32)(1000 / TPS))
+ return;
+ lastTime = g_system->getMillis();
+
+ switch (gameStatus.viewState) {
+ case V_IDLE: // Not processing state machine
+ intro().preNewGame(); // Any processing before New Game selected
+ break;
+ case V_INTROINIT: // Initialization before intro begins
+ intro().introInit();
+ g_system->showMouse(false);
+ gameStatus.viewState = V_INTRO;
+ break;
+ case V_INTRO: // Do any game-dependant preamble
+ if (intro().introPlay()) { // Process intro screen
+ scheduler().newScreen(0); // Initialize first screen
+ gameStatus.viewState = V_PLAY;
+ }
+ break;
+ case V_PLAY: // Playing game
+ g_system->showMouse(true);
+ parser().charHandler(); // Process user cmd input
+ moveObjects(); // Process object movement
+ scheduler().runScheduler(); // Process any actions
+ screen().displayList(D_RESTORE); // Restore previous background
+ updateImages(); // Draw into _frontBuffer, compile display list
+ mouse().mouseHandler(); // Mouse activity - adds to display list
+ screen().drawStatusText();
+ screen().displayList(D_DISPLAY); // Blit the display list to screen
+ break;
+ case V_INVENT: // Accessing inventory
+ inventory().runInventory(); // Process Inventory state machine
+ break;
+ case V_EXIT: // Game over or user exited
+ gameStatus.viewState = V_IDLE;
+ _status.doQuitFl = true;
+ break;
+ }
+}
+
+bool HugoEngine::loadHugoDat() {
+ Common::File in;
+ in.open("hugo.dat");
+
+ if (!in.isOpen()) {
+ Common::String errorMessage = "You're missing the 'hugo.dat' file. Get it from the ScummVM website";
+ GUIErrorMessage(errorMessage);
+ warning("%s", errorMessage.c_str());
+ return false;
+ }
+
+ // Read header
+ char buf[256];
+ in.read(buf, 4);
+ buf[4] = '\0';
+
+ if (strcmp(buf, "HUGO")) {
+ Common::String errorMessage = "File 'hugo.dat' is corrupt. Get it from the ScummVM website";
+ GUIErrorMessage(errorMessage);
+ warning("%s", errorMessage.c_str());
+ return false;
+ }
+
+ int majVer = in.readByte();
+ int minVer = in.readByte();
+
+ if ((majVer != HUGO_DAT_VER_MAJ) || (minVer != HUGO_DAT_VER_MIN)) {
+ snprintf(buf, 256, "File 'hugo.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", HUGO_DAT_VER_MAJ, HUGO_DAT_VER_MIN, majVer, minVer);
+ GUIErrorMessage(buf);
+ warning("%s", buf);
+
+ return false;
+ }
+
+ _numVariant = in.readUint16BE();
+
+ // Read textData
+ _textData = loadTextsVariante(in, 0);
+
+ // Read stringtData
+ // Only Hugo 1 DOS should use this array
+ _stringtData = loadTextsVariante(in, 0);
+
+ // Read arrayNouns
+ _arrayNouns = loadTextsArray(in);
+
+ // Read arrayVerbs
+ _arrayVerbs = loadTextsArray(in);
+
+ // Read screenNames
+ _screenNames = loadTextsVariante(in, &_numScreens);
+
+ // Read palette
+ _paletteSize = in.readUint16BE();
+ _palette = (byte *)malloc(sizeof(byte) * _paletteSize);
+ for (int i = 0; i < _paletteSize; i++)
+ _palette[i] = in.readByte();
+
+ // Read textEngine
+ _textEngine = loadTexts(in);
+
+ // Read textIntro
+ _textIntro = loadTextsVariante(in, 0);
+
+ // Read x_intro and y_intro
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ int numRows = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _introXSize = numRows;
+ _introX = (byte *)malloc(sizeof(byte) * _introXSize);
+ _introY = (byte *)malloc(sizeof(byte) * _introXSize);
+ for (int i = 0; i < _introXSize; i++) {
+ _introX[i] = in.readByte();
+ _introY[i] = in.readByte();
+ }
+ } else {
+ for (int i = 0; i < numRows; i++) {
+ in.readByte();
+ in.readByte();
+ }
+ }
+ }
+
+ // Read textMouse
+ _textMouse = loadTexts(in);
+
+ // Read textParser
+ _textParser = loadTexts(in);
+
+ // Read textSchedule
+ _textSchedule = loadTexts(in);
+
+ // Read textUtil
+ _textUtil = loadTexts(in);
+
+ // Read _arrayReqs
+ _arrayReqs = loadLongArray(in);
+
+ // Read _hotspots
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ int numRows = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _hotspots = (hotspot_t *)malloc(sizeof(hotspot_t) * numRows);
+ for (int i = 0; i < numRows; i++) {
+ _hotspots[i].screenIndex = in.readSint16BE();
+ _hotspots[i].x1 = in.readSint16BE();
+ _hotspots[i].y1 = in.readSint16BE();
+ _hotspots[i].x2 = in.readSint16BE();
+ _hotspots[i].y2 = in.readSint16BE();
+ _hotspots[i].actIndex = in.readUint16BE();
+ _hotspots[i].viewx = in.readSint16BE();
+ _hotspots[i].viewy = in.readSint16BE();
+ _hotspots[i].direction = in.readSint16BE();
+ }
+ } else {
+ for (int i = 0; i < numRows; i++) {
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ }
+ }
+ }
+
+ int numElem, numSubElem, numSubAct;
+ //Read _invent
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _maxInvent = numElem;
+ _invent = (int16 *)malloc(sizeof(int16) * numElem);
+ for (int i = 0; i < numElem; i++)
+ _invent[i] = in.readSint16BE();
+ } else {
+ for (int i = 0; i < numElem; i++)
+ in.readSint16BE();
+ }
+ }
+
+ //Read _uses
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _uses = (uses_t *)malloc(sizeof(uses_t) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ _uses[i].objId = in.readSint16BE();
+ _uses[i].dataIndex = in.readUint16BE();
+ numSubElem = in.readUint16BE();
+ _uses[i].targets = (target_t *)malloc(sizeof(target_t) * numSubElem);
+ for (int j = 0; j < numSubElem; j++) {
+ _uses[i].targets[j].nounIndex = in.readUint16BE();
+ _uses[i].targets[j].verbIndex = in.readUint16BE();
+ }
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ in.readSint16BE();
+ in.readUint16BE();
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ }
+ }
+ }
+ }
+
+ //Read _catchallList
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _catchallList = (background_t *)malloc(sizeof(background_t) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ _catchallList[i].verbIndex = in.readUint16BE();
+ _catchallList[i].nounIndex = in.readUint16BE();
+ _catchallList[i].commentIndex = in.readSint16BE();
+ _catchallList[i].matchFl = (in.readByte() != 0);
+ _catchallList[i].roomState = in.readByte();
+ _catchallList[i].bonusIndex = in.readByte();
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ }
+ }
+ }
+
+// Read _background_objects
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _backgroundObjects = (background_t **)malloc(sizeof(background_t *) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ _backgroundObjects[i] = (background_t *)malloc(sizeof(background_t) * numSubElem);
+ for (int j = 0; j < numSubElem; j++) {
+ _backgroundObjects[i][j].verbIndex = in.readUint16BE();
+ _backgroundObjects[i][j].nounIndex = in.readUint16BE();
+ _backgroundObjects[i][j].commentIndex = in.readSint16BE();
+ _backgroundObjects[i][j].matchFl = (in.readByte() != 0);
+ _backgroundObjects[i][j].roomState = in.readByte();
+ _backgroundObjects[i][j].bonusIndex = in.readByte();
+ }
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ }
+ }
+ }
+ }
+
+ // Read _points
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _numBonuses = numElem;
+ _points = (point_t *)malloc(sizeof(point_t) * _numBonuses);
+ for (int i = 0; i < _numBonuses; i++) {
+ _points[i].score = in.readByte();
+ _points[i].scoredFl = false;
+ }
+ } else {
+ for (int i = 0; i < numElem; i++)
+ in.readByte();
+ }
+ }
+
+ // Read _cmdList
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _cmdList = (cmd **)malloc(sizeof(cmd *) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ _cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem);
+ for (int j = 0; j < numSubElem; j++) {
+ _cmdList[i][j].verbIndex = in.readUint16BE();
+ _cmdList[i][j].reqIndex = in.readUint16BE();
+ _cmdList[i][j].textDataNoCarryIndex = in.readUint16BE();
+ _cmdList[i][j].reqState = in.readByte();
+ _cmdList[i][j].newState = in.readByte();
+ _cmdList[i][j].textDataWrongIndex = in.readUint16BE();
+ _cmdList[i][j].textDataDoneIndex = in.readUint16BE();
+ _cmdList[i][j].actIndex = in.readUint16BE();
+ }
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ }
+ }
+ }
+ }
+
+// TODO: For Hugo2 and Hugo3, if not in story mode, increment _screenActs[0][0] (ex: kALcrashStory + 1 == kALcrashNoStory)
+ // Read _screenActs
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _screenActs = (uint16 **)malloc(sizeof(uint16 *) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ if (numSubElem == 0) {
+ _screenActs[i] = 0;
+ } else {
+ _screenActs[i] = (uint16 *)malloc(sizeof(uint16) * numSubElem);
+ for (int j = 0; j < numSubElem; j++)
+ _screenActs[i][j] = in.readUint16BE();
+ }
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++)
+ in.readUint16BE();
+ }
+ }
+ }
+
+// TODO: For Hugo3, if not in story mode, set _objects[2].state to 3
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _objects = (object_t *)malloc(sizeof(object_t) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ _objects[i].nounIndex = in.readUint16BE();
+ _objects[i].dataIndex = in.readUint16BE();
+ numSubElem = in.readUint16BE();
+ if (numSubElem == 0)
+ _objects[i].stateDataIndex = 0;
+ else
+ _objects[i].stateDataIndex = (uint16 *)malloc(sizeof(uint16) * numSubElem);
+ for (int j = 0; j < numSubElem; j++)
+ _objects[i].stateDataIndex[j] = in.readUint16BE();
+ _objects[i].pathType = (path_t) in.readSint16BE();
+ _objects[i].vxPath = in.readSint16BE();
+ _objects[i].vyPath = in.readSint16BE();
+ _objects[i].actIndex = in.readUint16BE();
+ _objects[i].seqNumb = in.readByte();
+ _objects[i].currImagePtr = 0;
+ if (_objects[i].seqNumb == 0) {
+ _objects[i].seqList[0].imageNbr = 0;
+ _objects[i].seqList[0].seqPtr = 0;
+ }
+ for (int j = 0; j < _objects[i].seqNumb; j++) {
+ _objects[i].seqList[j].imageNbr = in.readUint16BE();
+ _objects[i].seqList[j].seqPtr = 0;
+ }
+ _objects[i].cycling = (cycle_t)in.readByte();
+ _objects[i].cycleNumb = in.readByte();
+ _objects[i].frameInterval = in.readByte();
+ _objects[i].frameTimer = in.readByte();
+ _objects[i].radius = in.readByte();
+ _objects[i].screenIndex = in.readByte();
+ _objects[i].x = in.readSint16BE();
+ _objects[i].y = in.readSint16BE();
+ _objects[i].oldx = in.readSint16BE();
+ _objects[i].oldy = in.readSint16BE();
+ _objects[i].vx = in.readByte();
+ _objects[i].vy = in.readByte();
+ _objects[i].objValue = in.readByte();
+ _objects[i].genericCmd = in.readSint16BE();
+ _objects[i].cmdIndex = in.readUint16BE();
+ _objects[i].carriedFl = (in.readByte() != 0);
+ _objects[i].state = in.readByte();
+ _objects[i].verbOnlyFl = (in.readByte() != 0);
+ _objects[i].priority = in.readByte();
+ _objects[i].viewx = in.readSint16BE();
+ _objects[i].viewy = in.readSint16BE();
+ _objects[i].direction = in.readSint16BE();
+ _objects[i].curSeqNum = in.readByte();
+ _objects[i].curImageNum = in.readByte();
+ _objects[i].oldvx = in.readByte();
+ _objects[i].oldvy = in.readByte();
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ in.readUint16BE();
+ in.readUint16BE();
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++)
+ in.readUint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ numSubElem = in.readByte();
+ for (int j = 0; j < numSubElem; j++)
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ }
+ }
+ }
+//#define HERO 0
+ _hero = &_objects[HERO]; // This always points to hero
+ _screen_p = &(_objects[HERO].screenIndex); // Current screen is hero's
+ _heroImage = HERO; // Current in use hero image
+
+//read _actListArr
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _actListArr = (act **)malloc(sizeof(act *) * numElem);
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ _actListArr[i] = (act *) malloc(sizeof(act) * (numSubElem + 1));
+ for (int j = 0; j < numSubElem; j++) {
+ _actListArr[i][j].a0.actType = (action_t) in.readByte();
+ switch (_actListArr[i][j].a0.actType) {
+ case ANULL: // -1
+ break;
+ case ASCHEDULE: // 0
+ _actListArr[i][j].a0.timer = in.readSint16BE();
+ _actListArr[i][j].a0.actIndex = in.readUint16BE();
+ break;
+ case START_OBJ: // 1
+ _actListArr[i][j].a1.timer = in.readSint16BE();
+ _actListArr[i][j].a1.objNumb = in.readSint16BE();
+ _actListArr[i][j].a1.cycleNumb = in.readSint16BE();
+ _actListArr[i][j].a1.cycle = (cycle_t) in.readByte();
+ break;
+ case INIT_OBJXY: // 2
+ _actListArr[i][j].a2.timer = in.readSint16BE();
+ _actListArr[i][j].a2.objNumb = in.readSint16BE();
+ _actListArr[i][j].a2.x = in.readSint16BE();
+ _actListArr[i][j].a2.y = in.readSint16BE();
+ break;
+ case PROMPT: // 3
+ _actListArr[i][j].a3.timer = in.readSint16BE();
+ _actListArr[i][j].a3.promptIndex = in.readSint16BE();
+ numSubAct = in.readUint16BE();
+ _actListArr[i][j].a3.responsePtr = (int *) malloc(sizeof(int) * numSubAct);
+ for (int k = 0; k < numSubAct; k++)
+ _actListArr[i][j].a3.responsePtr[k] = in.readSint16BE();
+ _actListArr[i][j].a3.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a3.actFailIndex = in.readUint16BE();
+ _actListArr[i][j].a3.encodedFl = (in.readByte() == 1) ? true : false;
+ break;
+ case BKGD_COLOR: // 4
+ _actListArr[i][j].a4.timer = in.readSint16BE();
+ _actListArr[i][j].a4.newBackgroundColor = in.readUint32BE();
+ break;
+ case INIT_OBJVXY: // 5
+ _actListArr[i][j].a5.timer = in.readSint16BE();
+ _actListArr[i][j].a5.objNumb = in.readSint16BE();
+ _actListArr[i][j].a5.vx = in.readSint16BE();
+ _actListArr[i][j].a5.vy = in.readSint16BE();
+ break;
+ case INIT_CARRY: // 6
+ _actListArr[i][j].a6.timer = in.readSint16BE();
+ _actListArr[i][j].a6.objNumb = in.readSint16BE();
+ _actListArr[i][j].a6.carriedFl = (in.readByte() == 1) ? true : false;
+ break;
+ case INIT_HF_COORD: // 7
+ _actListArr[i][j].a7.timer = in.readSint16BE();
+ _actListArr[i][j].a7.objNumb = in.readSint16BE();
+ break;
+ case NEW_SCREEN: // 8
+ _actListArr[i][j].a8.timer = in.readSint16BE();
+ _actListArr[i][j].a8.screenIndex = in.readSint16BE();
+ break;
+ case INIT_OBJSTATE: // 9
+ _actListArr[i][j].a9.timer = in.readSint16BE();
+ _actListArr[i][j].a9.objNumb = in.readSint16BE();
+ _actListArr[i][j].a9.newState = in.readByte();
+ break;
+ case INIT_PATH: // 10
+ _actListArr[i][j].a10.timer = in.readSint16BE();
+ _actListArr[i][j].a10.objNumb = in.readSint16BE();
+ _actListArr[i][j].a10.newPathType = in.readSint16BE();
+ _actListArr[i][j].a10.vxPath = in.readByte();
+ _actListArr[i][j].a10.vyPath = in.readByte();
+ break;
+ case COND_R: // 11
+ _actListArr[i][j].a11.timer = in.readSint16BE();
+ _actListArr[i][j].a11.objNumb = in.readSint16BE();
+ _actListArr[i][j].a11.stateReq = in.readByte();
+ _actListArr[i][j].a11.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a11.actFailIndex = in.readUint16BE();
+ break;
+ case TEXT: // 12
+ _actListArr[i][j].a12.timer = in.readSint16BE();
+ _actListArr[i][j].a12.stringIndex = in.readSint16BE();
+ break;
+ case SWAP_IMAGES: // 13
+ _actListArr[i][j].a13.timer = in.readSint16BE();
+ _actListArr[i][j].a13.obj1 = in.readSint16BE();
+ _actListArr[i][j].a13.obj2 = in.readSint16BE();
+ break;
+ case COND_SCR: // 14
+ _actListArr[i][j].a14.timer = in.readSint16BE();
+ _actListArr[i][j].a14.objNumb = in.readSint16BE();
+ _actListArr[i][j].a14.screenReq = in.readSint16BE();
+ _actListArr[i][j].a14.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a14.actFailIndex = in.readUint16BE();
+ break;
+ case AUTOPILOT: // 15
+ _actListArr[i][j].a15.timer = in.readSint16BE();
+ _actListArr[i][j].a15.obj1 = in.readSint16BE();
+ _actListArr[i][j].a15.obj2 = in.readSint16BE();
+ _actListArr[i][j].a15.dx = in.readByte();
+ _actListArr[i][j].a15.dy = in.readByte();
+ break;
+ case INIT_OBJ_SEQ: // 16
+ _actListArr[i][j].a16.timer = in.readSint16BE();
+ _actListArr[i][j].a16.objNumb = in.readSint16BE();
+ _actListArr[i][j].a16.seqIndex = in.readSint16BE();
+ break;
+ case SET_STATE_BITS: // 17
+ _actListArr[i][j].a17.timer = in.readSint16BE();
+ _actListArr[i][j].a17.objNumb = in.readSint16BE();
+ _actListArr[i][j].a17.stateMask = in.readSint16BE();
+ break;
+ case CLEAR_STATE_BITS: // 18
+ _actListArr[i][j].a18.timer = in.readSint16BE();
+ _actListArr[i][j].a18.objNumb = in.readSint16BE();
+ _actListArr[i][j].a18.stateMask = in.readSint16BE();
+ break;
+ case TEST_STATE_BITS: // 19
+ _actListArr[i][j].a19.timer = in.readSint16BE();
+ _actListArr[i][j].a19.objNumb = in.readSint16BE();
+ _actListArr[i][j].a19.stateMask = in.readSint16BE();
+ _actListArr[i][j].a19.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a19.actFailIndex = in.readUint16BE();
+ break;
+ case DEL_EVENTS: // 20
+ _actListArr[i][j].a20.timer = in.readSint16BE();
+ _actListArr[i][j].a20.actTypeDel = (action_t) in.readByte();
+ break;
+ case GAMEOVER: // 21
+ _actListArr[i][j].a21.timer = in.readSint16BE();
+ break;
+ case INIT_HH_COORD: // 22
+ _actListArr[i][j].a22.timer = in.readSint16BE();
+ _actListArr[i][j].a22.objNumb = in.readSint16BE();
+ break;
+ case EXIT: // 23
+ _actListArr[i][j].a23.timer = in.readSint16BE();
+ break;
+ case BONUS: // 24
+ _actListArr[i][j].a24.timer = in.readSint16BE();
+ _actListArr[i][j].a24.pointIndex = in.readSint16BE();
+ break;
+ case COND_BOX: // 25
+ _actListArr[i][j].a25.timer = in.readSint16BE();
+ _actListArr[i][j].a25.objNumb = in.readSint16BE();
+ _actListArr[i][j].a25.x1 = in.readSint16BE();
+ _actListArr[i][j].a25.y1 = in.readSint16BE();
+ _actListArr[i][j].a25.x2 = in.readSint16BE();
+ _actListArr[i][j].a25.y2 = in.readSint16BE();
+ _actListArr[i][j].a25.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a25.actFailIndex = in.readUint16BE();
+ break;
+ case SOUND: // 26
+ _actListArr[i][j].a26.timer = in.readSint16BE();
+ _actListArr[i][j].a26.soundIndex = in.readSint16BE();
+ break;
+ case ADD_SCORE: // 27
+ _actListArr[i][j].a27.timer = in.readSint16BE();
+ _actListArr[i][j].a27.objNumb = in.readSint16BE();
+ break;
+ case SUB_SCORE: // 28
+ _actListArr[i][j].a28.timer = in.readSint16BE();
+ _actListArr[i][j].a28.objNumb = in.readSint16BE();
+ break;
+ case COND_CARRY: // 29
+ _actListArr[i][j].a29.timer = in.readSint16BE();
+ _actListArr[i][j].a29.objNumb = in.readSint16BE();
+ _actListArr[i][j].a29.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a29.actFailIndex = in.readUint16BE();
+ break;
+ case INIT_MAZE: // 30
+ _actListArr[i][j].a30.timer = in.readSint16BE();
+ _actListArr[i][j].a30.mazeSize = in.readByte();
+ _actListArr[i][j].a30.x1 = in.readSint16BE();
+ _actListArr[i][j].a30.y1 = in.readSint16BE();
+ _actListArr[i][j].a30.x2 = in.readSint16BE();
+ _actListArr[i][j].a30.y2 = in.readSint16BE();
+ _actListArr[i][j].a30.x3 = in.readSint16BE();
+ _actListArr[i][j].a30.x4 = in.readSint16BE();
+ _actListArr[i][j].a30.firstScreenIndex = in.readByte();
+ break;
+ case EXIT_MAZE: // 31
+ _actListArr[i][j].a31.timer = in.readSint16BE();
+ break;
+ case INIT_PRIORITY: // 32
+ _actListArr[i][j].a32.timer = in.readSint16BE();
+ _actListArr[i][j].a32.objNumb = in.readSint16BE();
+ _actListArr[i][j].a32.priority = in.readByte();
+ break;
+ case INIT_SCREEN: // 33
+ _actListArr[i][j].a33.timer = in.readSint16BE();
+ _actListArr[i][j].a33.objNumb = in.readSint16BE();
+ _actListArr[i][j].a33.screenIndex = in.readSint16BE();
+ break;
+ case AGSCHEDULE: // 34
+ _actListArr[i][j].a34.timer = in.readSint16BE();
+ _actListArr[i][j].a34.actIndex = in.readUint16BE();
+ break;
+ case REMAPPAL: // 35
+ _actListArr[i][j].a35.timer = in.readSint16BE();
+ _actListArr[i][j].a35.oldColorIndex = in.readSint16BE();
+ _actListArr[i][j].a35.newColorIndex = in.readSint16BE();
+ break;
+ case COND_NOUN: // 36
+ _actListArr[i][j].a36.timer = in.readSint16BE();
+ _actListArr[i][j].a36.nounIndex = in.readUint16BE();
+ _actListArr[i][j].a36.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a36.actFailIndex = in.readUint16BE();
+ break;
+ case SCREEN_STATE: // 37
+ _actListArr[i][j].a37.timer = in.readSint16BE();
+ _actListArr[i][j].a37.screenIndex = in.readSint16BE();
+ _actListArr[i][j].a37.newState = in.readByte();
+ break;
+ case INIT_LIPS: // 38
+ _actListArr[i][j].a38.timer = in.readSint16BE();
+ _actListArr[i][j].a38.lipsObjNumb = in.readSint16BE();
+ _actListArr[i][j].a38.objNumb = in.readSint16BE();
+ _actListArr[i][j].a38.dxLips = in.readByte();
+ _actListArr[i][j].a38.dyLips = in.readByte();
+ break;
+ case INIT_STORY_MODE: // 39
+ _actListArr[i][j].a39.timer = in.readSint16BE();
+ _actListArr[i][j].a39.storyModeFl = (in.readByte() == 1);
+ break;
+ case WARN: // 40
+ _actListArr[i][j].a40.timer = in.readSint16BE();
+ _actListArr[i][j].a40.stringIndex = in.readSint16BE();
+ break;
+ case COND_BONUS: // 41
+ _actListArr[i][j].a41.timer = in.readSint16BE();
+ _actListArr[i][j].a41.BonusIndex = in.readSint16BE();
+ _actListArr[i][j].a41.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a41.actFailIndex = in.readUint16BE();
+ break;
+ case TEXT_TAKE: // 42
+ _actListArr[i][j].a42.timer = in.readSint16BE();
+ _actListArr[i][j].a42.objNumb = in.readSint16BE();
+ break;
+ case YESNO: // 43
+ _actListArr[i][j].a43.timer = in.readSint16BE();
+ _actListArr[i][j].a43.promptIndex = in.readSint16BE();
+ _actListArr[i][j].a43.actYesIndex = in.readUint16BE();
+ _actListArr[i][j].a43.actNoIndex = in.readUint16BE();
+ break;
+ case STOP_ROUTE: // 44
+ _actListArr[i][j].a44.timer = in.readSint16BE();
+ break;
+ case COND_ROUTE: // 45
+ _actListArr[i][j].a45.timer = in.readSint16BE();
+ _actListArr[i][j].a45.routeIndex = in.readSint16BE();
+ _actListArr[i][j].a45.actPassIndex = in.readUint16BE();
+ _actListArr[i][j].a45.actFailIndex = in.readUint16BE();
+ break;
+ case INIT_JUMPEXIT: // 46
+ _actListArr[i][j].a46.timer = in.readSint16BE();
+ _actListArr[i][j].a46.jumpExitFl = (in.readByte() == 1);
+ break;
+ case INIT_VIEW: // 47
+ _actListArr[i][j].a47.timer = in.readSint16BE();
+ _actListArr[i][j].a47.objNumb = in.readSint16BE();
+ _actListArr[i][j].a47.viewx = in.readSint16BE();
+ _actListArr[i][j].a47.viewy = in.readSint16BE();
+ _actListArr[i][j].a47.direction = in.readSint16BE();
+ break;
+ case INIT_OBJ_FRAME: // 48
+ _actListArr[i][j].a48.timer = in.readSint16BE();
+ _actListArr[i][j].a48.objNumb = in.readSint16BE();
+ _actListArr[i][j].a48.seqIndex = in.readSint16BE();
+ _actListArr[i][j].a48.frameIndex = in.readSint16BE();
+ break;
+ case OLD_SONG: //49
+ _actListArr[i][j].a49.timer = in.readSint16BE();
+ _actListArr[i][j].a49.soundIndex = in.readUint16BE();
+ break;
+ default:
+ error("Engine - Unknown action type encountered: %d", _actListArr[i][j].a0.actType);
+ }
+ }
+ _actListArr[i][numSubElem].a0.actType = ANULL;
+ }
+ } else {
+ for (int i = 0; i < numElem; i++) {
+ numSubElem = in.readUint16BE();
+ for (int j = 0; j < numSubElem; j++) {
+ numSubAct = in.readByte();
+ switch (numSubAct) {
+ case ANULL: // -1
+ break;
+ case ASCHEDULE: // 0
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ case START_OBJ: // 1
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_OBJXY: // 2
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case PROMPT: // 3
+ in.readSint16BE();
+ in.readSint16BE();
+ numSubAct = in.readUint16BE();
+ for (int k = 0; k < numSubAct; k++)
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readByte();
+ break;
+ case BKGD_COLOR: // 4
+ in.readSint16BE();
+ in.readUint32BE();
+ break;
+ case INIT_OBJVXY: // 5
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_CARRY: // 6
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_HF_COORD: // 7
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case NEW_SCREEN: // 8
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_OBJSTATE: // 9
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_PATH: // 10
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case COND_R: // 11
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case TEXT: // 12
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SWAP_IMAGES: // 13
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_SCR: // 14
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case AUTOPILOT: // 15
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case INIT_OBJ_SEQ: // 16
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SET_STATE_BITS: // 17
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case CLEAR_STATE_BITS: // 18
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case TEST_STATE_BITS: // 19
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case DEL_EVENTS: // 20
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case GAMEOVER: // 21
+ in.readSint16BE();
+ break;
+ case INIT_HH_COORD: // 22
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case EXIT: // 23
+ in.readSint16BE();
+ break;
+ case BONUS: // 24
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_BOX: // 25
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case SOUND: // 26
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case ADD_SCORE: // 27
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case SUB_SCORE: // 28
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_CARRY: // 29
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case INIT_MAZE: // 30
+ in.readSint16BE();
+ in.readByte();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case EXIT_MAZE: // 31
+ in.readSint16BE();
+ break;
+ case INIT_PRIORITY: // 32
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_SCREEN: // 33
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case AGSCHEDULE: // 34
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ case REMAPPAL: // 35
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_NOUN: // 36
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case SCREEN_STATE: // 37
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_LIPS: // 38
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readByte();
+ in.readByte();
+ break;
+ case INIT_STORY_MODE: // 39
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case WARN: // 40
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case COND_BONUS: // 41
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case TEXT_TAKE: // 42
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case YESNO: // 43
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case STOP_ROUTE: // 44
+ in.readSint16BE();
+ break;
+ case COND_ROUTE: // 45
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ break;
+ case INIT_JUMPEXIT: // 46
+ in.readSint16BE();
+ in.readByte();
+ break;
+ case INIT_VIEW: // 47
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case INIT_OBJ_FRAME: // 48
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ in.readSint16BE();
+ break;
+ case OLD_SONG: //49
+ in.readSint16BE();
+ in.readUint16BE();
+ break;
+ default:
+ error("Engine - Unknown action type encountered %d - variante %d pos %d.%d", numSubAct, varnt, i, j);
+ }
+ }
+ }
+ }
+ }
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ if (varnt == _gameVariant) {
+ _tunesNbr = in.readByte();
+ _soundSilence = in.readByte();
+ _soundTest = in.readByte();
+ } else {
+ in.readByte();
+ in.readByte();
+ in.readByte();
+ }
+ }
+
+ //Read _defltTunes
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _maxInvent = numElem;
+ _defltTunes = (int16 *)malloc(sizeof(int16) * numElem);
+ for (int i = 0; i < numElem; i++)
+ _defltTunes[i] = in.readSint16BE();
+ } else {
+ for (int i = 0; i < numElem; i++)
+ in.readSint16BE();
+ }
+ }
+
+ //Read _screenStates size
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ _screenStates = (byte *)malloc(sizeof(byte) * numElem);
+ for (int i = 0; i < numElem; i++)
+ _screenStates[i] = 0;
+ }
+ }
+
+ //Read look, take and drop special verbs indexes
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ if (varnt == _gameVariant) {
+ _look = in.readUint16BE();
+ _take = in.readUint16BE();
+ _drop = in.readUint16BE();
+ } else {
+ in.readUint16BE();
+ in.readUint16BE();
+ in.readUint16BE();
+ }
+ }
+
+ //Read LASTOBJ
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant)
+ _numObj = numElem;
+ }
+
+ //Read kALnewscr used by maze (Hugo 2)
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numElem = in.readUint16BE();
+ if (varnt == _gameVariant)
+ _alNewscrIndex = numElem;
+ }
+
+ if (_gameVariant > 2) {
+ _arrayFontSize[0] = in.readUint16BE();
+ _arrayFont[0] = (byte *)malloc(sizeof(byte) * _arrayFontSize[0]);
+ for (int j = 0; j < _arrayFontSize[0]; j++)
+ _arrayFont[0][j] = in.readByte();
+
+ _arrayFontSize[1] = in.readUint16BE();
+ _arrayFont[1] = (byte *)malloc(sizeof(byte) * _arrayFontSize[1]);
+ for (int j = 0; j < _arrayFontSize[1]; j++)
+ _arrayFont[1][j] = in.readByte();
+
+ _arrayFontSize[2] = in.readUint16BE();
+ _arrayFont[2] = (byte *)malloc(sizeof(byte) * _arrayFontSize[2]);
+ for (int j = 0; j < _arrayFontSize[2]; j++)
+ _arrayFont[2][j] = in.readByte();
+ } else {
+ numElem = in.readUint16BE();
+ for (int j = 0; j < numElem; j++)
+ in.readByte();
+
+ numElem = in.readUint16BE();
+ for (int j = 0; j < numElem; j++)
+ in.readByte();
+
+ numElem = in.readUint16BE();
+ for (int j = 0; j < numElem; j++)
+ in.readByte();
+ }
+ return true;
+}
+
+char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) {
+ int numTexts;
+ int entryLen;
+ int len;
+ char **res = 0;
+ char *pos = 0;
+
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numTexts = in.readUint16BE();
+ entryLen = in.readUint16BE();
+ pos = (char *)malloc(entryLen);
+ if (varnt == _gameVariant) {
+ if (arraySize)
+ *arraySize = numTexts;
+ res = (char **)malloc(sizeof(char *) * numTexts);
+ res[0] = pos;
+ in.read(res[0], entryLen);
+ res[0] += DATAALIGNMENT;
+ } else {
+ in.read(pos, entryLen);
+ }
+
+ pos += DATAALIGNMENT;
+
+ for (int i = 1; i < numTexts; i++) {
+ pos -= 2;
+
+ len = READ_BE_UINT16(pos);
+ pos += 2 + len;
+
+ if (varnt == _gameVariant)
+ res[i] = pos;
+ }
+ }
+
+ return res;
+}
+
+uint16 **HugoEngine::loadLongArray(Common::File &in) {
+ uint16 **resArray = 0;
+
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ uint16 numRows = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ resArray = (uint16 **)malloc(sizeof(uint16 *) * (numRows + 1));
+ resArray[numRows] = 0;
+ }
+ for (int i = 0; i < numRows; i++) {
+ uint16 numElems = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ uint16 *resRow = (uint16 *)malloc(sizeof(uint16) * numElems);
+ for (int j = 0; j < numElems; j++)
+ resRow[j] = in.readUint16BE();
+ resArray[i] = resRow;
+ } else {
+ for (int j = 0; j < numElems; j++)
+ in.readUint16BE();
+ }
+ }
+ }
+ return resArray;
+}
+
+char ***HugoEngine::loadTextsArray(Common::File &in) {
+ char ***resArray = 0;
+
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ int numNouns = in.readUint16BE();
+ if (varnt == _gameVariant) {
+ resArray = (char ** *)malloc(sizeof(char **) * (numNouns + 1));
+ resArray[numNouns] = 0;
+ }
+ for (int i = 0; i < numNouns; i++) {
+ int numTexts = in.readUint16BE();
+ int entryLen = in.readUint16BE();
+ char *pos = (char *)malloc(entryLen);
+ char **res = 0;
+ if (varnt == _gameVariant) {
+ res = (char **)malloc(sizeof(char *) * numTexts);
+ res[0] = pos;
+ in.read(res[0], entryLen);
+ res[0] += DATAALIGNMENT;
+ } else {
+ in.read(pos, entryLen);
+ }
+
+ pos += DATAALIGNMENT;
+
+ for (int j = 0; j < numTexts; j++) {
+ if (varnt == _gameVariant)
+ res[j] = pos;
+
+ pos -= 2;
+ int len = READ_BE_UINT16(pos);
+ pos += 2 + len;
+ }
+
+ if (varnt == _gameVariant)
+ resArray[i] = res;
+ }
+ }
+
+ return resArray;
+}
+
+char **HugoEngine::loadTexts(Common::File &in) {
+ int numTexts = in.readUint16BE();
+ char **res = (char **)malloc(sizeof(char *) * numTexts);
+ int entryLen = in.readUint16BE();
+ char *pos = (char *)malloc(entryLen);
+
+ in.read(pos, entryLen);
+
+ pos += DATAALIGNMENT;
+ res[0] = pos;
+
+ for (int i = 1; i < numTexts; i++) {
+ pos -= 2;
+ int len = READ_BE_UINT16(pos);
+ pos += 2 + len;
+ res[i] = pos;
+ }
+
+ return res;
+}
+
+void HugoEngine::freeTexts(char **ptr) {
+ if (!ptr)
+ return;
+
+ free(*ptr);
+ free(ptr);
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h
new file mode 100644
index 0000000000..1d3657b473
--- /dev/null
+++ b/engines/hugo/hugo.h
@@ -0,0 +1,325 @@
+/* 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 HUGO_H
+#define HUGO_H
+
+#include "engines/engine.h"
+#include "common/file.h"
+
+// This include is here temporarily while the engine is being refactored.
+#include "hugo/game.h"
+
+#define HUGO_DAT_VER_MAJ 0 // 1 byte
+#define HUGO_DAT_VER_MIN 25 // 1 byte
+#define DATAALIGNMENT 4
+
+namespace Common {
+class RandomSource;
+}
+
+namespace Hugo {
+enum GameType {
+ kGameTypeNone = 0,
+ kGameTypeHugo1,
+ kGameTypeHugo2,
+ kGameTypeHugo3
+};
+
+enum GameVariant {
+ kGameVariantH1Win = 0,
+ kGameVariantH2Win,
+ kGameVariantH3Win,
+ kGameVariantH1Dos,
+ kGameVariantH2Dos,
+ kGameVariantH3Dos
+};
+
+enum HugoDebugChannels {
+ kDebugSchedule = 1 << 0,
+ kDebugEngine = 1 << 1,
+ kDebugDisplay = 1 << 2,
+ kDebugMouse = 1 << 3,
+ kDebugParser = 1 << 4,
+ kDebugFile = 1 << 5,
+ kDebugRoute = 1 << 6,
+ kDebugInventory = 1 << 7
+};
+
+enum HugoGameFeatures {
+ GF_PACKED = (1 << 0) // Database
+};
+
+struct HugoGameDescription;
+
+class FileManager;
+class Scheduler;
+class Screen;
+class MouseHandler;
+class InventoryHandler;
+class Parser;
+class Route;
+class SoundHandler;
+class IntroHandler;
+
+class HugoEngine : public Engine {
+public:
+ HugoEngine(OSystem *syst, const HugoGameDescription *gd);
+ ~HugoEngine();
+
+ byte _numVariant;
+ byte _gameVariant;
+ byte _maxInvent;
+ byte _numBonuses;
+ byte _soundSilence;
+ byte _soundTest;
+ byte _tunesNbr;
+ uint16 _numScreens;
+
+ object_t *_hero;
+ byte *_screen_p;
+ byte _heroImage;
+
+ byte *_palette;
+ byte *_introX;
+ byte *_introY;
+ byte *_screenStates;
+ byte *_arrayFont[3];
+ int16 _arrayFontSize[3];
+ char **_textData;
+ char **_stringtData;
+ char **_screenNames;
+ char **_textEngine;
+ char **_textIntro;
+ char **_textMouse;
+ char **_textParser;
+ char **_textSchedule;
+ char **_textUtil;
+ char ***_arrayNouns;
+ char ***_arrayVerbs;
+ uint16 **_arrayReqs;
+ hotspot_t *_hotspots;
+ int16 *_invent;
+ uses_t *_uses;
+ background_t *_catchallList;
+ background_t **_backgroundObjects;
+ point_t *_points;
+ cmd **_cmdList;
+ uint16 **_screenActs;
+ object_t *_objects;
+ act **_actListArr;
+ int16 *_defltTunes;
+ uint16 _look;
+ uint16 _take;
+ uint16 _drop;
+ uint16 _numObj;
+ uint16 _alNewscrIndex;
+
+ Common::RandomSource *_rnd;
+
+ const char *_episode;
+ const char *_picDir;
+
+ Common::String _initFilename, _saveFilename;
+
+ command_t _statusLine;
+ command_t _scoreLine;
+
+ const HugoGameDescription *_gameDescription;
+ uint32 getFeatures() const;
+
+ GameType getGameType() const;
+ Common::Platform getPlatform() const;
+ bool isPacked() const;
+
+ // Temporary, until the engine is fully objectified.
+ static HugoEngine &get() {
+ assert(s_Engine != 0);
+ return *s_Engine;
+ }
+
+ FileManager &file() {
+ return *_fileManager;
+ }
+ Scheduler &scheduler() {
+ return *_scheduler;
+ }
+ Screen &screen() {
+ return *_screen;
+ }
+ MouseHandler &mouse() {
+ return *_mouseHandler;
+ }
+ InventoryHandler &inventory() {
+ return *_inventoryHandler;
+ }
+ Parser &parser() {
+ return *_parser;
+ }
+ Route &route() {
+ return *_route;
+ }
+ SoundHandler &sound() {
+ return *_soundHandler;
+ }
+ IntroHandler &intro() {
+ return *_introHandler;
+ }
+
+ void initGame(const HugoGameDescription *gd);
+ void initGamePart(const HugoGameDescription *gd);
+ bool loadHugoDat();
+
+ int getMouseX() const {
+ return _mouseX;
+ }
+ int getMouseY() const {
+ return _mouseY;
+ }
+
+ void initStatus();
+ void readObjectImages();
+ void readUIFImages();
+ void updateImages();
+ void moveObjects();
+ void useObject(int16 objId);
+ bool findObjectSpace(object_t *obj, int16 *destx, int16 *desty);
+ int16 findObject(uint16 x, uint16 y);
+ void lookObject(object_t *obj);
+ void storeBoundary(int x1, int x2, int y);
+ void clearBoundary(int x1, int x2, int y);
+ void endGame();
+ void readScreenFiles(int screen);
+ void setNewScreen(int screen);
+ void initNewScreenDisplay();
+ void screenActions(int screen);
+ void shutdown();
+
+ overlay_t &getBoundaryOverlay() {
+ return _boundary;
+ }
+ overlay_t &getObjectBoundaryOverlay() {
+ return _objBound;
+ }
+ overlay_t &getBaseBoundaryOverlay() {
+ return _ovlBase;
+ }
+ overlay_t &getFirstOverlay() {
+ return _overlay;
+ }
+
+ status_t &getGameStatus() {
+ return _status;
+ }
+
+ int getScore() const {
+ return _score;
+ }
+ void setScore(int newScore) {
+ _score = newScore;
+ }
+ void adjustScore(int adjustment) {
+ _score += adjustment;
+ }
+ int getMaxScore() const {
+ return _maxscore;
+ }
+ void setMaxScore(int newScore) {
+ _maxscore = newScore;
+ }
+ byte getIntroSize() {
+ return _introXSize;
+ }
+
+protected:
+
+ // Engine APIs
+ Common::Error run();
+
+private:
+ int _mouseX;
+ int _mouseY;
+ byte _paletteSize;
+ byte _introXSize;
+ status_t _status; // Game status structure
+
+ static HugoEngine *s_Engine;
+
+// The following are bit plane display overlays which mark travel boundaries,
+// foreground stationary objects and baselines for those objects (used to
+// determine foreground/background wrt moving objects)
+
+// Vinterstum: These shouldn't be static, but we get weird pathfinding issues (and Valgrind warnings) without.
+// Needs more investigation. Alignment issues?
+
+ static overlay_t _boundary; // Boundary overlay file
+ static overlay_t _overlay; // First overlay file
+ static overlay_t _ovlBase; // First overlay base file
+ static overlay_t _objBound; // Boundary file marks object baselines
+
+ GameType _gameType;
+ Common::Platform _platform;
+ bool _packedFl;
+
+ FileManager *_fileManager;
+ Scheduler *_scheduler;
+ Screen *_screen;
+ MouseHandler *_mouseHandler;
+ InventoryHandler *_inventoryHandler;
+ Parser *_parser;
+ Route *_route;
+ SoundHandler *_soundHandler;
+ IntroHandler *_introHandler;
+
+ int _score; // Holds current score
+ int _maxscore; // Holds maximum score
+
+ char **loadTextsVariante(Common::File &in, uint16 *arraySize);
+ char ***loadTextsArray(Common::File &in);
+ uint16 **loadLongArray(Common::File &in);
+ char **loadTexts(Common::File &in);
+ void freeTexts(char **ptr);
+
+ void initPlaylist(bool playlist[MAX_TUNES]);
+ void initConfig(inst_t action);
+ void initialize();
+ int deltaX(int x1, int x2, int vx, int y);
+ int deltaY(int x1, int x2, int vy, int y);
+ void processMaze();
+ //int y2comp (const void *a, const void *b);
+ char *useBG(char *name);
+ void freeObjects();
+ void boundaryCollision(object_t *obj);
+ void calcMaxScore();
+ void initMachine();
+ void runMachine();
+
+ static int y2comp(const void *a, const void *b);
+
+};
+
+} // End of namespace Hugo
+
+#endif // Hugo_H
diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp
new file mode 100644
index 0000000000..b818bef027
--- /dev/null
+++ b/engines/hugo/intro.cpp
@@ -0,0 +1,46 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+
+namespace Hugo {
+
+IntroHandler::IntroHandler(HugoEngine &vm) : _vm(vm) {
+}
+
+IntroHandler::~IntroHandler() {
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro.h b/engines/hugo/intro.h
new file mode 100644
index 0000000000..f8f1f95514
--- /dev/null
+++ b/engines/hugo/intro.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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef INTRO_H
+#define INTRO_H
+
+namespace Hugo {
+
+enum seqTextIntro {
+ kIntro1 = 0,
+ kIntro2 = 1,
+ kIntro3 = 2
+};
+
+class IntroHandler {
+public:
+ IntroHandler(HugoEngine &vm);
+ virtual ~IntroHandler();
+
+ virtual void preNewGame() = 0;
+ virtual void introInit() = 0;
+ virtual bool introPlay() = 0;
+
+protected:
+ HugoEngine &_vm;
+ int16 introTicks; // Count calls to introPlay()
+};
+
+class intro_v1w : public IntroHandler {
+public:
+ intro_v1w(HugoEngine &vm);
+ ~intro_v1w();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+class intro_v1d : public IntroHandler {
+public:
+ intro_v1d(HugoEngine &vm);
+ ~intro_v1d();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+class intro_v2w : public IntroHandler {
+public:
+ intro_v2w(HugoEngine &vm);
+ ~intro_v2w();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+class intro_v2d : public IntroHandler {
+public:
+ intro_v2d(HugoEngine &vm);
+ ~intro_v2d();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+class intro_v3w : public IntroHandler {
+public:
+ intro_v3w(HugoEngine &vm);
+ ~intro_v3w();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+class intro_v3d : public IntroHandler {
+public:
+ intro_v3d(HugoEngine &vm);
+ ~intro_v3d();
+
+ void preNewGame();
+ void introInit();
+ bool introPlay();
+};
+
+} // End of namespace Hugo
+
+#endif
diff --git a/engines/hugo/intro_v1d.cpp b/engines/hugo/intro_v1d.cpp
new file mode 100644
index 0000000000..0e3067f0e9
--- /dev/null
+++ b/engines/hugo/intro_v1d.cpp
@@ -0,0 +1,172 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+#include "hugo/display.h"
+
+namespace Hugo {
+intro_v1d::intro_v1d(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v1d::~intro_v1d() {
+}
+
+void intro_v1d::preNewGame() {
+}
+
+void intro_v1d::introInit() {
+ introTicks = 0;
+}
+
+bool intro_v1d::introPlay() {
+ static int state = 0;
+ byte introSize = _vm.getIntroSize();
+
+ if (introTicks < introSize) {
+ switch (state++) {
+ case 0:
+ _vm.screen().drawRectangle(true, 0, 0, 319, 199, _TMAGENTA);
+ _vm.screen().drawRectangle(true, 10, 10, 309, 189, _TBLACK);
+ break;
+
+ case 1:
+ _vm.screen().drawShape(20, 92,_TLIGHTMAGENTA,_TMAGENTA);
+ _vm.screen().drawShape(250,92,_TLIGHTMAGENTA,_TMAGENTA);
+
+ // HACK: use of TROMAN, size 10-5
+ _vm.screen().loadFont(0);
+
+ char buffer[80];
+ if (_boot.registered)
+ strcpy(buffer, "Registered Version");
+ else
+ strcpy(buffer, "Shareware Version");
+ _vm.screen().writeStr(CENTER, 163, buffer, _TLIGHTMAGENTA);
+ _vm.screen().writeStr(CENTER, 176, COPYRIGHT, _TLIGHTMAGENTA);
+
+ if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
+ sprintf(buffer, "Distributed by %s.", _boot.distrib);
+ _vm.screen().writeStr(CENTER, 75, buffer, _TMAGENTA);
+ }
+
+ // HACK: use of SCRIPT size 24-16
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "Hugo's");
+ _vm.screen().writeStr(CENTER, 20, buffer, _TMAGENTA);
+
+ // HACK: use of TROMAN, size 30-24
+ strcpy(buffer, "House of Horrors !");
+ _vm.screen().writeStr(CENTER, 50, buffer, _TLIGHTMAGENTA);
+ break;
+ case 2:
+ _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ // HACK: use of TROMAN, size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "S t a r r i n g :");
+ _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ break;
+ case 3:
+ // HACK: use of TROMAN size 20-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "Hugo !");
+ _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ break;
+ case 4:
+ _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "P r o d u c e d b y :");
+ _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ break;
+ case 5:
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "David P Gray !");
+ _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ break;
+ case 6:
+ _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "D i r e c t e d b y :");
+ _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ break;
+ case 7:
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "David P Gray !");
+ _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ break;
+ case 8:
+ _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "M u s i c b y :");
+ _vm.screen().writeStr(CENTER, 95, buffer, _TMAGENTA);
+ break;
+ case 9:
+ // HACK: use of TROMAN size 16-9
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "David P Gray !");
+ _vm.screen().writeStr(CENTER, 115, buffer, _TLIGHTMAGENTA);
+ break;
+ case 10:
+ _vm.screen().drawRectangle(true, 82, 92, 237, 138, _TBLACK);
+ // HACK: use of TROMAN size 20-14
+ _vm.screen().loadFont(2);
+
+ strcpy(buffer, "E n j o y !");
+ _vm.screen().writeStr(CENTER, 100, buffer, _TLIGHTMAGENTA);
+ break;
+ }
+
+ _vm.screen().displayBackground();
+ g_system->updateScreen();
+ g_system->delayMillis(1000);
+ }
+
+ return (++introTicks >= introSize);
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro_v1w.cpp b/engines/hugo/intro_v1w.cpp
new file mode 100644
index 0000000000..38921fd3e4
--- /dev/null
+++ b/engines/hugo/intro_v1w.cpp
@@ -0,0 +1,61 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+#include "hugo/file.h"
+
+
+
+namespace Hugo {
+intro_v1w::intro_v1w(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v1w::~intro_v1w() {
+}
+
+void intro_v1w::preNewGame() {
+ // Auto-start a new game
+ _vm.file().restoreGame(-1);
+ _vm.getGameStatus().viewState = V_INTROINIT;
+}
+
+void intro_v1w::introInit() {
+}
+
+bool intro_v1w::introPlay() {
+ return true;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro_v2d.cpp b/engines/hugo/intro_v2d.cpp
new file mode 100644
index 0000000000..bfae11b77e
--- /dev/null
+++ b/engines/hugo/intro_v2d.cpp
@@ -0,0 +1,77 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+
+namespace Hugo {
+
+intro_v2d::intro_v2d(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v2d::~intro_v2d() {
+}
+
+void intro_v2d::preNewGame() {
+}
+
+void intro_v2d::introInit() {
+ _vm.screen().loadFont(0);
+ _vm.file().readBackground(_vm._numScreens - 1); // display splash screen
+
+ char buffer[128];
+
+ if (_boot.registered)
+ sprintf(buffer, "%s Registered Version", COPYRIGHT);
+ else
+ sprintf(buffer, "%s Shareware Version", COPYRIGHT);
+ _vm.screen().writeStr(CENTER, 186, buffer, _TLIGHTRED);
+
+ if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
+ sprintf(buffer, "Distributed by %s.", _boot.distrib);
+ _vm.screen().writeStr(CENTER, 1, buffer, _TLIGHTRED);
+ }
+
+ _vm.screen().displayBackground();
+ g_system->updateScreen();
+ g_system->delayMillis(5000);
+}
+
+bool intro_v2d::introPlay() {
+ return true;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro_v2w.cpp b/engines/hugo/intro_v2w.cpp
new file mode 100644
index 0000000000..f68761eac9
--- /dev/null
+++ b/engines/hugo/intro_v2w.cpp
@@ -0,0 +1,57 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+
+
+namespace Hugo {
+
+intro_v2w::intro_v2w(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v2w::~intro_v2w() {
+}
+
+void intro_v2w::preNewGame() {
+}
+
+void intro_v2w::introInit() {
+}
+
+bool intro_v2w::introPlay() {
+ return true;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro_v3d.cpp b/engines/hugo/intro_v3d.cpp
new file mode 100644
index 0000000000..3dd2a58d8b
--- /dev/null
+++ b/engines/hugo/intro_v3d.cpp
@@ -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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+
+namespace Hugo {
+intro_v3d::intro_v3d(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v3d::~intro_v3d() {
+}
+
+void intro_v3d::preNewGame() {
+}
+
+void intro_v3d::introInit() {
+ _vm.screen().loadFont(0);
+ _vm.file().readBackground(_vm._numScreens - 1); // display splash screen
+
+ char buffer[128];
+ if (_boot.registered)
+ sprintf(buffer, "%s Registered Version", COPYRIGHT);
+ else
+ sprintf(buffer,"%s Shareware Version", COPYRIGHT);
+
+ _vm.screen().writeStr(CENTER, 190, buffer, _TBROWN);
+
+ if (scumm_stricmp(_boot.distrib, "David P. Gray")) {
+ sprintf(buffer, "Distributed by %s.", _boot.distrib);
+ _vm.screen().writeStr(CENTER, 0, buffer, _TBROWN);
+ }
+
+ _vm.screen().displayBackground();
+ g_system->updateScreen();
+ g_system->delayMillis(5000);
+
+ _vm.file().readBackground(22); // display screen MAP_3d
+ _vm.screen().displayBackground();
+ introTicks = 0;
+}
+
+bool intro_v3d::introPlay() {
+ byte introSize = _vm.getIntroSize();
+
+// Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
+// Called every tick. Returns TRUE when complete
+//TODO : Add proper check of story mode
+//#if STORY
+ if (introTicks < introSize) {
+ _vm.screen().writeStr(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
+ _vm.screen().displayBackground();
+
+ // Text boxes at various times
+ switch (introTicks) {
+ case 4:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro1]);
+ break;
+ case 9:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro2]);
+ break;
+ case 35:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro3]);
+ break;
+ }
+ }
+
+ return (++introTicks >= introSize);
+//#else //STORY
+// return true;
+//#endif //STORY
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/intro_v3w.cpp b/engines/hugo/intro_v3w.cpp
new file mode 100644
index 0000000000..924fa46805
--- /dev/null
+++ b/engines/hugo/intro_v3w.cpp
@@ -0,0 +1,94 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+intro_v3w::intro_v3w(HugoEngine &vm) : IntroHandler(vm) {
+}
+
+intro_v3w::~intro_v3w() {
+}
+
+void intro_v3w::preNewGame() {
+}
+
+void intro_v3w::introInit() {
+// Hugo 3 - show map and set up for introPlay()
+//#if STORY
+ _vm.file().readBackground(22); // display screen MAP_3w
+ _vm.screen().displayBackground();
+ introTicks = 0;
+ _vm.screen().loadFont(0);
+//#endif
+}
+
+bool intro_v3w::introPlay() {
+ byte introSize = _vm.getIntroSize();
+
+// Hugo 3 - Preamble screen before going into game. Draws path of Hugo's plane.
+// Called every tick. Returns TRUE when complete
+//TODO : Add proper check of story mode
+//#if STORY
+ if (introTicks < introSize) {
+ // Scale viewport x_intro,y_intro to screen (offsetting y)
+ _vm.screen().writeStr(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, "x", _TBRIGHTWHITE);
+ _vm.screen().displayBackground();
+
+
+ // Text boxes at various times
+ switch (introTicks) {
+ case 4:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro1]);
+ break;
+ case 9:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro2]);
+ break;
+ case 35:
+ Utils::Box(BOX_OK, "%s", _vm._textIntro[kIntro3]);
+ break;
+ }
+ }
+
+ return (++introTicks >= introSize);
+//#else //STORY
+// return true;
+//#endif //STORY
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/inventory.cpp b/engines/hugo/inventory.cpp
new file mode 100644
index 0000000000..5046d191c3
--- /dev/null
+++ b/engines/hugo/inventory.cpp
@@ -0,0 +1,231 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/game.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/mouse.h"
+#include "hugo/inventory.h"
+#include "hugo/parser.h"
+
+namespace Hugo {
+
+#define MAX_DISP (XPIX / INV_DX) // Max icons displayable
+
+InventoryHandler::InventoryHandler(HugoEngine &vm) : _vm(vm) {
+}
+
+// Construct the inventory scrollbar in dib_i
+// imageTotNumb is total number of inventory icons
+// displayNumb is number requested for display
+// scrollFl is TRUE if scroll arrows required
+// firstObjId is index of first (scrolled) inventory object to display
+void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId) {
+ debugC(1, kDebugInventory, "constructInventory(%d, %d, %d, %d)", imageTotNumb, displayNumb, (scrollFl) ? 0 : 1, firstObjId);
+
+ // Clear out icon buffer
+ memset(_vm.screen().getIconBuffer(), 0, sizeof(_vm.screen().getIconBuffer()));
+
+ // If needed, copy arrows - reduce number of icons displayable
+ if (scrollFl) { // Display at first and last icon positions
+ _vm.screen().moveImage(_vm.screen().getGUIBuffer(), 0, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), 0, 0, XPIX);
+ _vm.screen().moveImage(_vm.screen().getGUIBuffer(), INV_DX, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), INV_DX *(MAX_DISP - 1), 0, XPIX);
+ displayNumb = MIN(displayNumb, MAX_DISP - NUM_ARROWS);
+ } else // No, override first index - we can show 'em all!
+ firstObjId = 0;
+
+ // Copy inventory icons to remaining positions
+ int16 displayed = 0;
+ int16 carried = 0;
+ for (int16 i = 0; i < imageTotNumb; i++) {
+ if (_vm._objects[_vm._invent[i]].carriedFl) {
+ // Check still room to display and past first scroll index
+ if (displayed < displayNumb && carried >= firstObjId) {
+ // Compute source coordinates in dib_u
+ int16 ux = (i + NUM_ARROWS) * INV_DX % XPIX;
+ int16 uy = (i + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
+
+ // Compute dest coordinates in dib_i
+ int16 ix = ((scrollFl) ? displayed + 1 : displayed) * INV_DX;
+ displayed++; // Count number displayed
+
+ // Copy the icon
+ _vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), ix, 0, XPIX);
+ }
+ carried++; // Count number carried
+ }
+ }
+}
+
+// Process required action for inventory
+// Returns objId under cursor (or -1) for INV_GET
+int16 InventoryHandler::processInventory(invact_t action, ...) {
+ debugC(1, kDebugInventory, "processInventory(invact_t action, ...)");
+
+ static int16 firstIconId = 0; // Index of first icon to display
+
+ int16 imageNumb; // Total number of inventory items
+ int displayNumb; // Total number displayed/carried
+ // Compute total number and number displayed, i.e. number carried
+ for (imageNumb = 0, displayNumb = 0; imageNumb < _vm._maxInvent && _vm._invent[imageNumb] != -1; imageNumb++) {
+ if (_vm._objects[_vm._invent[imageNumb]].carriedFl)
+ displayNumb++;
+ }
+
+ // Will we need the scroll arrows?
+ bool scrollFl = displayNumb > MAX_DISP;
+ va_list marker; // Args used for D_ADD operation
+ int16 cursorx, cursory; // Current cursor position
+ int16 objId = -1; // Return objid under cursor
+
+ switch (action) {
+ case INV_INIT: // Initialize inventory display
+ constructInventory(imageNumb, displayNumb, scrollFl, firstIconId);
+ break;
+ case INV_LEFT: // Scroll left by one icon
+ firstIconId = MAX(0, firstIconId - 1);
+ constructInventory(imageNumb, displayNumb, scrollFl, firstIconId);
+ break;
+ case INV_RIGHT: // Scroll right by one icon
+ firstIconId = MIN(displayNumb, firstIconId + 1);
+ constructInventory(imageNumb, displayNumb, scrollFl, firstIconId);
+ break;
+ case INV_GET: // Return object id under cursor
+ // Get cursor position from variable argument list
+ va_start(marker, action); // Initialize variable arguments
+ cursorx = va_arg(marker, int); // Cursor x
+ cursory = va_arg(marker, int); // Cursor y
+ va_end(marker); // Reset variable arguments
+
+ cursory -= DIBOFF_Y; // Icon bar is at true zero
+ if (cursory > 0 && cursory < INV_DY) { // Within icon bar?
+ int16 i = cursorx / INV_DX; // Compute icon index
+ if (scrollFl) { // Scroll buttons displayed
+ if (i == 0) { // Left scroll button
+ objId = LEFT_ARROW;
+ } else {
+ if (i == MAX_DISP - 1) // Right scroll button
+ objId = RIGHT_ARROW;
+ else // Adjust for scroll
+ i += firstIconId - 1; // i is icon index
+ }
+ }
+
+ // If not an arrow, find object id - limit to valid range
+ if (objId == -1 && i < displayNumb) {
+ // Find objid by counting # carried objects == i+1
+ int16 j;
+ for (j = 0, i++; i > 0 && j < _vm._numObj; j++) {
+ if (_vm._objects[j].carriedFl) {
+ if (--i == 0)
+ objId = j;
+ }
+ }
+ }
+ }
+ break;
+ }
+ return objId; // For the INV_GET action
+}
+
+void InventoryHandler::runInventory() {
+ status_t &gameStatus = _vm.getGameStatus();
+
+ debugC(1, kDebugInventory, "runInventory");
+
+// Process inventory state machine
+ switch (gameStatus.inventoryState) {
+ case I_OFF: // Icon bar off screen
+ break;
+ case I_UP: // Icon bar moving up
+ gameStatus.inventoryHeight -= STEP_DY; // Move the icon bar up
+ if (gameStatus.inventoryHeight <= 0) // Limit travel
+ gameStatus.inventoryHeight = 0;
+
+ // Move visible portion to _frontBuffer, restore uncovered portion, display results
+ _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX, STEP_DY, XPIX, _vm.screen().getFrontBuffer(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX);
+ _vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight + STEP_DY);
+
+ if (gameStatus.inventoryHeight == 0) { // Finished moving up?
+ // Yes, restore dibs and exit back to game state machine
+ _vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX);
+ _vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getFrontBuffer(), 0, 0, XPIX);
+ _vm.updateImages(); // Add objects back into display list for restore
+ gameStatus.inventoryState = I_OFF;
+ gameStatus.viewState = V_PLAY;
+ }
+ break;
+ case I_DOWN: // Icon bar moving down
+ // If this is the first step, initialize dib_i
+ // and get any icon/text out of _frontBuffer
+ if (gameStatus.inventoryHeight == 0) {
+ processInventory(INV_INIT); // Initialize dib_i
+ _vm.screen().displayList(D_RESTORE); // Restore _frontBuffer
+ _vm.updateImages(); // Rebuild _frontBuffer without icons/text
+ _vm.screen().displayList(D_DISPLAY); // Blit display list to screen
+ }
+
+ gameStatus.inventoryHeight += STEP_DY; // Move the icon bar down
+ if (gameStatus.inventoryHeight >= INV_DY) // Limit travel
+ gameStatus.inventoryHeight = INV_DY;
+
+ // Move visible portion to _frontBuffer, display results
+ _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight);
+
+ if (gameStatus.inventoryHeight == INV_DY) { // Finished moving down?
+ // Yes, prepare view dibs for special inventory display since
+ // we can't refresh objects while icon bar overlayed...
+ // 1. Save backing store _backBuffer in temporary dib_c
+ // 2. Make snapshot of _frontBuffer the new _backBuffer backing store
+ // 3. Reset the display list
+ _vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBufferBackup(), 0, 0, XPIX);
+ _vm.screen().moveImage(_vm.screen().getFrontBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX);
+ _vm.screen().displayList(D_INIT);
+ gameStatus.inventoryState = I_ACTIVE;
+ }
+ break;
+ case I_ACTIVE: // Inventory active
+ _vm.parser().charHandler(); // Still allow commands
+ _vm.screen().displayList(D_RESTORE); // Restore previous background
+ _vm.mouse().mouseHandler(); // Mouse activity - adds to display list
+ _vm.screen().displayList(D_DISPLAY); // Blit the display list to screen
+ break;
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/inventory.h b/engines/hugo/inventory.h
new file mode 100644
index 0000000000..5cc1af28c2
--- /dev/null
+++ b/engines/hugo/inventory.h
@@ -0,0 +1,56 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_INVENTORY_H
+#define HUGO_INVENTORY_H
+namespace Hugo {
+
+#define NUM_ARROWS 2 // Number of arrows (left/right)
+#define LEFT_ARROW -2 // Cursor over Left arrow in inventory icon bar
+#define RIGHT_ARROW -3 // Cursor over Right arrow in inventory icon bar
+
+class InventoryHandler {
+public:
+ InventoryHandler(HugoEngine &vm);
+
+ int16 processInventory(invact_t action, ...);
+ void runInventory();
+
+private:
+ HugoEngine &_vm;
+
+ void constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId);
+};
+
+} // End of namespace Hugo
+
+#endif // HUGO_INVENTORY_H
diff --git a/engines/hugo/module.mk b/engines/hugo/module.mk
new file mode 100644
index 0000000000..b646a3672d
--- /dev/null
+++ b/engines/hugo/module.mk
@@ -0,0 +1,42 @@
+MODULE := engines/hugo
+
+MODULE_OBJS := \
+ detection.o \
+ display.o \
+ display_v1d.o \
+ display_v1w.o \
+ engine.o \
+ file.o \
+ file_v1d.o \
+ file_v2d.o \
+ file_v3d.o \
+ file_v1w.o \
+ hugo.o \
+ intro.o \
+ intro_v1d.o \
+ intro_v2d.o \
+ intro_v3d.o \
+ intro_v1w.o \
+ intro_v2w.o \
+ intro_v3w.o \
+ inventory.o \
+ mouse.o \
+ parser.o \
+ parser_v1w.o \
+ parser_v1d.o \
+ parser_v2d.o \
+ parser_v3d.o \
+ route.o \
+ schedule.o \
+ schedule_v1d.o \
+ schedule_v3d.o \
+ sound.o \
+ util.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_HUGO), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/hugo/mouse.cpp b/engines/hugo/mouse.cpp
new file mode 100644
index 0000000000..b305489568
--- /dev/null
+++ b/engines/hugo/mouse.cpp
@@ -0,0 +1,303 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// mouse.cpp : Handle all mouse activity
+
+#include "common/system.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/mouse.h"
+#include "hugo/global.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/inventory.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+#define EXIT_HOTSPOT -4 // Cursor over Exit hotspot
+#define CURSOR_NAME 2 // Index of name used under cursor
+#define CURSOR_NOCHAR '~' // Don't show name of object under cursor
+#define SX_OFF 10 // Cursor offset to name string
+#define SY_OFF -2 // Cursor offset to name string
+#define IX_OFF 8 // Cursor to icon image (dib coords)
+#define IY_OFF 10 // Cursor to icon image (dib coords)
+
+enum seqTextMouse {
+ kMsNoWayText = 0,
+ kMsExit = 1
+};
+
+MouseHandler::MouseHandler(HugoEngine &vm) : _vm(vm) {
+}
+
+// Shadow-blit supplied string into dib_a at cx,cy and add to display list
+void MouseHandler::cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color) {
+ debugC(1, kDebugMouse, "cursorText(%s, %d, %d, %d, %d)", buffer, cx, cy, fontId, color);
+
+ _vm.screen().loadFont(fontId);
+
+ // Find bounding rect for string
+ int16 sdx = _vm.screen().stringLength(buffer);
+ int16 sdy = _vm.screen().fontHeight() + 1; // + 1 for shadow
+ int16 sx = (cx < XPIX / 2) ? cx + SX_OFF : cx - sdx - SX_OFF / 2;
+ int16 sy = cy + SY_OFF;
+
+ // Display the string and add rect to display list
+ _vm.screen().shadowStr(sx, sy, buffer, _TBRIGHTWHITE);
+ _vm.screen().displayList(D_ADD, sx, sy, sdx, sdy);
+}
+
+// Find the exit hotspot containing cx, cy.
+// Return hotspot index or -1 if not found.
+int16 MouseHandler::findExit(int16 cx, int16 cy) {
+ debugC(2, kDebugMouse, "findExit(%d, %d)", cx, cy);
+
+ int i = 0;
+ for (hotspot_t *hotspot = _vm._hotspots; hotspot->screenIndex >= 0; i++, hotspot++) {
+ if (hotspot->screenIndex == *_vm._screen_p) {
+ if (cx >= hotspot->x1 && cx <= hotspot->x2 && cy >= hotspot->y1 && cy <= hotspot->y2)
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Process a mouse right click at coord cx, cy over object objid
+void MouseHandler::processRightClick(int16 objId, int16 cx, int16 cy) {
+ debugC(1, kDebugMouse, "Process_rclick(%d, %d, %d)", objId, cx, cy);
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ return;
+
+ bool foundFl = false; // TRUE if route found to object
+ // Check if this was over iconbar
+ if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y) { // Clicked over iconbar object
+ if (gameStatus.inventoryObjId == -1)
+ gameStatus.inventoryObjId = objId; // Not using so select new object
+ else if (gameStatus.inventoryObjId == objId)
+ gameStatus.inventoryObjId = -1; // Same icon - deselect it
+ else
+ _vm.useObject(objId); // Use status.objid on object
+ } else { // Clicked over viewport object
+ object_t *obj = &_vm._objects[objId];
+ int16 x, y;
+ switch (obj->viewx) { // Where to walk to
+ case -1: // Walk to object position
+ if (_vm.findObjectSpace(obj, &x, &y))
+ foundFl = _vm.route().startRoute(GO_GET, objId, x, y);
+ if (!foundFl) // Can't get there, try to use from here
+ _vm.useObject(objId);
+ break;
+ case 0: // Immediate use
+ _vm.useObject(objId); // Pick up or use object
+ break;
+ default: // Walk to view point if possible
+ if (!_vm.route().startRoute(GO_GET, objId, obj->viewx, obj->viewy)) {
+ if (_vm._hero->cycling == INVISIBLE)// If invisible do
+ _vm.useObject(objId); // immediate use
+ else
+ Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ }
+ break;
+ }
+ }
+}
+
+// Process a left mouse click over:
+// 1. An icon - show description
+// 2. An object - walk to and show description
+// 3. An icon scroll arrow - scroll the iconbar
+// 4. Nothing - attempt to walk there
+// 5. Exit - walk to exit hotspot
+void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) {
+ debugC(1, kDebugMouse, "Process_lclick(%d, %d, %d)", objId, cx, cy);
+
+ int16 i, x, y;
+ object_t *obj;
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET) // Make sure user has control
+ return;
+
+ switch (objId) {
+ case -1: // Empty space - attempt to walk there
+ _vm.route().startRoute(GO_SPACE, 0, cx, cy);
+ break;
+ case LEFT_ARROW: // A scroll arrow - scroll the iconbar
+ case RIGHT_ARROW:
+ // Scroll the iconbar and display results
+ _vm.inventory().processInventory((objId == LEFT_ARROW) ? INV_LEFT : INV_RIGHT);
+ _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getBackBuffer(), 0, DIBOFF_Y, XPIX);
+ _vm.screen().displayList(D_ADD, 0, DIBOFF_Y, XPIX, INV_DY);
+ break;
+ case EXIT_HOTSPOT: // Walk to exit hotspot
+ i = findExit(cx, cy);
+ x = _vm._hotspots[i].viewx;
+ y = _vm._hotspots[i].viewy;
+ if (x >= 0) { // Hotspot refers to an exit
+ // Special case of immediate exit
+ if (gameStatus.jumpExitFl) {
+ // Get rid of iconbar if necessary
+ if (gameStatus.inventoryState != I_OFF)
+ gameStatus.inventoryState = I_UP;
+ _vm.scheduler().insertActionList(_vm._hotspots[i].actIndex);
+ } else { // Set up route to exit spot
+ if (_vm._hotspots[i].direction == Common::KEYCODE_RIGHT)
+ x -= HERO_MAX_WIDTH;
+ else if (_vm._hotspots[i].direction == Common::KEYCODE_LEFT)
+ x += HERO_MAX_WIDTH;
+ if (!_vm.route().startRoute(GO_EXIT, i, x, y))
+ Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ }
+
+ // Get rid of any attached icon
+ gameStatus.inventoryObjId = -1;
+ }
+ break;
+ default: // Look at an icon or object
+ obj = &_vm._objects[objId];
+
+ // Over iconbar - immediate description
+ if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y)
+ _vm.lookObject(obj);
+ else {
+ bool foundFl = false; // TRUE if route found to object
+ switch (obj->viewx) { // Clicked over viewport object
+ case -1: // Walk to object position
+ if (_vm.findObjectSpace(obj, &x, &y))
+ foundFl = _vm.route().startRoute(GO_LOOK, objId, x, y);
+ if (!foundFl) // Can't get there, immediate description
+ _vm.lookObject(obj);
+ break;
+ case 0: // Immediate description
+ _vm.lookObject(obj);
+ break;
+ default: // Walk to view point if possible
+ if (!_vm.route().startRoute(GO_LOOK, objId, obj->viewx, obj->viewy)) {
+ if (_vm._hero->cycling == INVISIBLE) // If invisible do
+ _vm.lookObject(obj); // immediate decription
+ else
+ Utils::Box(BOX_ANY, "%s", _vm._textMouse[kMsNoWayText]); // Can't get there
+ }
+ break;
+ }
+ }
+ break;
+ }
+}
+
+// Process mouse activity
+void MouseHandler::mouseHandler() {
+ debugC(2, kDebugMouse, "mouseHandler");
+
+ int16 cx = _vm.getMouseX();
+ int16 cy = _vm.getMouseY();
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ gameStatus.cx = cx; // Save cursor coords
+ gameStatus.cy = cy;
+
+ // Don't process if outside client area
+ if (cx < 0 || cx > XPIX || cy < DIBOFF_Y || cy > VIEW_DY + DIBOFF_Y)
+ return;
+
+ // Display dragged inventory icon if one currently selected
+ if (gameStatus.inventoryObjId != -1) {
+ // Find index of icon
+ int16 iconId; // Find index of dragged icon
+ for (iconId = 0; iconId < _vm._maxInvent; iconId++) {
+ if (gameStatus.inventoryObjId == _vm._invent[iconId])
+ break;
+ }
+
+ // Compute source coordinates in dib_u
+ int16 ux = (iconId + NUM_ARROWS) * INV_DX % XPIX;
+ int16 uy = (iconId + NUM_ARROWS) * INV_DX / XPIX * INV_DY;
+
+ // Compute destination coordinates in dib_a
+ int iconx = cx + IX_OFF;
+ int icony = cy + IY_OFF;
+ iconx = MAX(iconx, 0); // Keep within dib_a bounds
+ iconx = MIN(iconx, XPIX - INV_DX);
+ icony = MAX(icony, 0);
+ icony = MIN(icony, YPIX - INV_DY);
+
+ // Copy the icon and add to display list
+ _vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), iconx, icony, XPIX);
+ _vm.screen().displayList(D_ADD, iconx, icony, INV_DX, INV_DY);
+ }
+
+ int16 objId = -1; // Current source object
+ // Process cursor over an object or icon
+ if (gameStatus.inventoryState == I_ACTIVE) // Check inventory icon bar first
+ objId = _vm.inventory().processInventory(INV_GET, cx, cy);
+ if (objId == -1) // No match, check rest of view
+ objId = _vm.findObject(cx, cy);
+ if (objId >= 0) { // Got a match
+ // Display object name next to cursor (unless CURSOR_NOCHAR)
+ // Note test for swapped hero name
+ char *name = _vm._arrayNouns[_vm._objects[(objId == HERO) ? _vm._heroImage : objId].nounIndex][CURSOR_NAME];
+ if (name[0] != CURSOR_NOCHAR)
+ cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE);
+
+ // Process right click over object in view or iconbar
+ if (gameStatus.rightButtonFl)
+ processRightClick(objId, cx, cy);
+ }
+
+ // Process cursor over an exit hotspot
+ if (objId == -1) {
+ int i = findExit(cx, cy);
+ if (i != -1 && _vm._hotspots[i].viewx >= 0) {
+ objId = EXIT_HOTSPOT;
+ cursorText(_vm._textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE);
+ }
+ }
+
+ // Left click over icon, object or to move somewhere
+ if (gameStatus.leftButtonFl)
+ processLeftClick(objId, cx, cy);
+
+ // Clear mouse click states
+ gameStatus.leftButtonFl = false;
+ gameStatus.rightButtonFl = false;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/mouse.h b/engines/hugo/mouse.h
new file mode 100644
index 0000000000..3ac5f19f32
--- /dev/null
+++ b/engines/hugo/mouse.h
@@ -0,0 +1,54 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_MOUSE_H
+#define HUGO_MOUSE_H
+namespace Hugo {
+
+class MouseHandler {
+public:
+ MouseHandler(HugoEngine &vm);
+
+ void mouseHandler();
+
+private:
+ HugoEngine &_vm;
+
+ void cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color);
+ int16 findExit(int16 cx, int16 cy);
+ void processRightClick(int16 objId, int16 cx, int16 cy);
+ void processLeftClick(int16 objId, int16 cx, int16 cy);
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_MOUSE_H
diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp
new file mode 100644
index 0000000000..4277d6e845
--- /dev/null
+++ b/engines/hugo/parser.cpp
@@ -0,0 +1,305 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/parser.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+#define BLINKS 2 // Cursor blinks per second
+#define CX(X) LOWORD(X)
+#define CY(Y) HIWORD(Y)
+
+Parser::Parser(HugoEngine &vm) :
+ _vm(vm), _putIndex(0), _getIndex(0), _checkDoubleF1Fl(false) {
+}
+
+Parser::~Parser() {
+}
+
+void Parser::keyHandler(uint16 nChar, uint16 nFlags) {
+ debugC(1, kDebugParser, "keyHandler(%d, %d)", nChar, nFlags);
+
+ status_t &gameStatus = _vm.getGameStatus();
+ bool repeatedFl = (nFlags & 0x4000); // TRUE if key is a repeat
+
+// Process key down event - called from OnKeyDown()
+ switch (nChar) { // Set various toggle states
+ case Common::KEYCODE_ESCAPE: // Escape key, may want to QUIT
+ if (gameStatus.inventoryState == I_ACTIVE) // Remove inventory, if displayed
+ gameStatus.inventoryState = I_UP;
+ gameStatus.inventoryObjId = -1; // Deselect any dragged icon
+ break;
+ case Common::KEYCODE_END:
+ case Common::KEYCODE_HOME:
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_DOWN:
+ if (!repeatedFl) {
+ gameStatus.routeIndex = -1; // Stop any automatic route
+ _vm.route().setWalk(nChar); // Direction of hero travel
+ }
+ break;
+ case Common::KEYCODE_F1: // User Help (DOS)
+ if (_checkDoubleF1Fl)
+ _vm.file().instructions();
+ else
+ _vm.screen().userHelp();
+ _checkDoubleF1Fl = !_checkDoubleF1Fl;
+ break;
+ case Common::KEYCODE_F6: // Inventory
+ showDosInventory();
+ break;
+ case Common::KEYCODE_F8: // Turbo mode
+ _config.turboFl = !_config.turboFl;
+ break;
+ case Common::KEYCODE_F2: // Toggle sound
+ _vm.sound().toggleSound();
+ _vm.sound().toggleMusic();
+ break;
+ case Common::KEYCODE_F3: // Repeat last line
+ gameStatus.recallFl = true;
+ break;
+ case Common::KEYCODE_F4: // Save game
+ case Common::KEYCODE_F5: // Restore game
+ case Common::KEYCODE_F9: // Boss button
+ warning("STUB: KeyHandler() - F4-F5-F9 (DOS)");
+ break;
+ default: // Any other key
+ if (!gameStatus.storyModeFl) { // Keyboard disabled
+ // Add printable keys to ring buffer
+
+ uint16 bnext = _putIndex + 1;
+ if (bnext >= sizeof(_ringBuffer))
+ bnext = 0;
+ if (bnext != _getIndex) {
+ _ringBuffer[_putIndex] = nChar;
+ _putIndex = bnext;
+ }
+ }
+ break;
+ }
+ if (_checkDoubleF1Fl && (nChar != Common::KEYCODE_F1))
+ _checkDoubleF1Fl = false;
+}
+
+// Add any new chars to line buffer and display them.
+// If CR pressed, pass line to LineHandler()
+void Parser::charHandler() {
+ debugC(4, kDebugParser, "charHandler");
+
+ static int16 lineIndex = 0; // Index into line
+ static uint32 tick = 0; // For flashing cursor
+ static char cursor = '_';
+ static command_t cmdLine; // Build command line
+ status_t &gameStatus = _vm.getGameStatus();
+
+ // Check for one or more characters in ring buffer
+ while (_getIndex != _putIndex) {
+ char c = _ringBuffer[_getIndex++];
+ if (_getIndex >= sizeof(_ringBuffer))
+ _getIndex = 0;
+
+ switch (c) {
+ case Common::KEYCODE_BACKSPACE: // Rubout key
+ if (lineIndex)
+ cmdLine[--lineIndex] = '\0';
+ break;
+ case Common::KEYCODE_RETURN: // EOL, pass line to line handler
+ if (lineIndex && (_vm._hero->pathType != QUIET)) {
+ // Remove inventory bar if active
+ if (gameStatus.inventoryState == I_ACTIVE)
+ gameStatus.inventoryState = I_UP;
+ // Call Line handler and reset line
+ command(cmdLine);
+ cmdLine[lineIndex = 0] = '\0';
+ }
+ break;
+ default: // Normal text key, add to line
+ if (lineIndex >= MAX_CHARS) {
+ //MessageBeep(MB_ICONASTERISK);
+ warning("STUB: MessageBeep(MB_ICONASTERISK);");
+ } else if (isprint(c)) {
+ cmdLine[lineIndex++] = c;
+ cmdLine[lineIndex] = '\0';
+ }
+ break;
+ }
+ }
+
+ // See if time to blink cursor, set cursor character
+ if ((tick++ % (TPS / BLINKS)) == 0)
+ cursor = (cursor == '_') ? ' ' : '_';
+
+ // See if recall button pressed
+ if (gameStatus.recallFl) {
+ // Copy previous line to current cmdline
+ gameStatus.recallFl = false;
+ strcpy(cmdLine, _line);
+ lineIndex = strlen(cmdLine);
+ }
+
+ sprintf(_vm._statusLine, ">%s%c", cmdLine, cursor);
+ sprintf(_vm._scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_config.turboFl) ? "T" : " ", _vm.getScore(), _vm.getMaxScore(), (_config.soundFl) ? "On" : "Off");
+
+ // See if "look" button pressed
+ if (gameStatus.lookFl) {
+ command("look around");
+ gameStatus.lookFl = false;
+ }
+}
+
+// Perform an immediate command. Takes parameters a la sprintf
+// Assumes final string will not overrun line[] length
+void Parser::command(const char *format, ...) {
+ debugC(1, kDebugParser, "Command(%s, ...)", format);
+
+ va_list marker;
+ va_start(marker, format);
+ vsprintf(_line, format, marker);
+ va_end(marker);
+
+ lineHandler();
+}
+
+// Locate any member of object name list appearing in command line
+bool Parser::isWordPresent(char **wordArr) {
+ debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]);
+
+ if (wordArr != 0) {
+ for (int i = 0; strlen(wordArr[i]); i++) {
+ if (strstr(_line, wordArr[i]))
+ return true;
+ }
+ }
+ return false;
+}
+
+// Locate word in list of nouns and return ptr to first string in noun list
+char *Parser::findNoun() {
+ debugC(1, kDebugParser, "findNoun()");
+
+ for (int i = 0; _vm._arrayNouns[i]; i++) {
+ for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) {
+ if (strstr(_line, _vm._arrayNouns[i][j]))
+ return _vm._arrayNouns[i][0];
+ }
+ }
+ return 0;
+}
+
+// Locate word in list of verbs and return ptr to first string in verb list
+char *Parser::findVerb() {
+ debugC(1, kDebugParser, "findVerb()");
+
+ for (int i = 0; _vm._arrayVerbs[i]; i++) {
+ for (int j = 0; strlen(_vm._arrayVerbs[i][j]); j++) {
+ if (strstr(_line, _vm._arrayVerbs[i][j]))
+ return _vm._arrayVerbs[i][0];
+ }
+ }
+ return 0;
+}
+
+// Describe any takeable objects visible in this screen
+void Parser::showTakeables() {
+ debugC(1, kDebugParser, "showTakeables");
+
+ for (int j = 0; j < _vm._numObj; j++) {
+ object_t *obj = &_vm._objects[j];
+ if ((obj->cycling != INVISIBLE) &&
+ (obj->screenIndex == *_vm._screen_p) &&
+ (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) {
+ Utils::Box(BOX_ANY, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]);
+ }
+ }
+}
+
+// Return TRUE if object being carried by hero
+bool Parser::isCarrying(uint16 wordIndex) {
+ debugC(1, kDebugParser, "isCarrying(%d)", wordIndex);
+
+ for (int i = 0; i < _vm._numObj; i++) {
+ if ((wordIndex == _vm._objects[i].nounIndex) && _vm._objects[i].carriedFl)
+ return true;
+ }
+ return false;
+}
+
+// Show user all objects being carried in a variable width 2 column format
+void Parser::showDosInventory() {
+ debugC(1, kDebugParser, "showDosInventory()");
+ static const char *blanks = " ";
+ uint16 index = 0, len1 = 0, len2 = 0;
+
+ for (int i = 0; i < _vm._numObj; i++) { // Find widths of 2 columns
+ if (_vm._objects[i].carriedFl) {
+ uint16 len = strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]);
+ if (index++ & 1) // Right hand column
+ len2 = (len > len2) ? len : len2;
+ else
+ len1 = (len > len1) ? len : len1;
+ }
+ }
+ len1 += 1; // For gap between columns
+
+ if (len1 + len2 < (uint16)strlen(_vm._textParser[kTBOutro]))
+ len1 = strlen(_vm._textParser[kTBOutro]);
+
+ char buffer[XBYTES *NUM_ROWS] = "\0";
+ strncat(buffer, blanks, (len1 + len2 - strlen(_vm._textParser[kTBIntro])) / 2);
+ strcat(strcat(buffer, _vm._textParser[kTBIntro]), "\n");
+ index = 0;
+ for (int i = 0; i < _vm._numObj; i++) { // Assign strings
+ if (_vm._objects[i].carriedFl) {
+ if (index++ & 1)
+ strcat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), "\n");
+ else
+ strncat(strcat(buffer, _vm._arrayNouns[_vm._objects[i].nounIndex][1]), blanks, len1 - strlen(_vm._arrayNouns[_vm._objects[i].nounIndex][1]));
+ }
+ }
+ if (index & 1)
+ strcat(buffer, "\n");
+ strcat(buffer, _vm._textParser[kTBOutro]);
+
+ Utils::Box(BOX_ANY, "%s", buffer);
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser.h b/engines/hugo/parser.h
new file mode 100644
index 0000000000..5226304d51
--- /dev/null
+++ b/engines/hugo/parser.h
@@ -0,0 +1,132 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_PARSER_H
+#define HUGO_PARSER_H
+namespace Hugo {
+
+enum seqTextParser {
+ kTBExit = 0, kTBMaze, kTBNoPoint, kTBNoun, kTBVerb,
+ kTBEh, kTBUnusual, kTBHave, kTBNoUse, kTBDontHave,
+ kTBNeed, kTBOk, kCmtAny1, kCmtAny2, kCmtAny3,
+ kCmtClose, kTBIntro, kTBOutro, kTBUnusual_1d, kCmtAny4,
+ kCmtAny5, kTBExit_1d, kTBEh_1d, kTBEh_2d, kTBNoUse_2d
+};
+
+class Parser {
+public:
+ Parser(HugoEngine &vm);
+ virtual ~Parser();
+
+ bool isWordPresent(char **wordArr);
+
+ void charHandler();
+ void command(const char *format, ...);
+ void keyHandler(uint16 nChar, uint16 nFlags);
+ virtual void lineHandler() = 0;
+
+protected:
+ HugoEngine &_vm;
+
+protected:
+ bool isCarrying(uint16 wordIndex);
+
+ char *findNoun();
+ char *findVerb();
+
+ void showTakeables();
+
+private:
+ char _ringBuffer[32]; // Ring buffer
+ uint16 _putIndex;
+ uint16 _getIndex; // Index into ring buffer
+ bool _checkDoubleF1Fl; // Flag used to display user help or instructions
+
+ void showDosInventory();
+};
+
+class Parser_v1w : public Parser {
+public:
+ Parser_v1w(HugoEngine &vm);
+ ~Parser_v1w();
+
+ virtual void lineHandler();
+
+protected:
+ bool isBackgroundWord(objectList_t obj);
+ bool isCatchallVerb(objectList_t obj);
+ bool isGenericVerb(object_t *obj, char *comment);
+ bool isObjectVerb(object_t *obj, char *comment);
+ void takeObject(object_t *obj);
+
+private:
+ bool isNear(object_t *obj, char *verb, char *comment);
+ void dropObject(object_t *obj);
+};
+
+class Parser_v1d : public Parser {
+public:
+ Parser_v1d(HugoEngine &vm);
+ ~Parser_v1d();
+
+ virtual void lineHandler();
+
+protected:
+ bool isNear(char *verb, char *noun, object_t *obj, char *comment);
+ bool isGenericVerb(char *word, object_t *obj);
+ bool isObjectVerb(char *word, object_t *obj);
+ bool isBackgroundWord(char *noun, char *verb, objectList_t obj);
+ bool isCatchallVerb(bool testNounFl, char *noun, char *verb, objectList_t obj);
+ char *findNextNoun(char *noun);
+ void dropObject(object_t *obj);
+ void takeObject(object_t *obj);
+};
+
+class Parser_v2d : public Parser_v1d {
+public:
+ Parser_v2d(HugoEngine &vm);
+ ~Parser_v2d();
+
+ void lineHandler();
+};
+
+class Parser_v3d : public Parser_v1w {
+public:
+ Parser_v3d(HugoEngine &vm);
+ ~Parser_v3d();
+
+ void lineHandler();
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_PARSER_H
diff --git a/engines/hugo/parser_v1d.cpp b/engines/hugo/parser_v1d.cpp
new file mode 100644
index 0000000000..9364cd9532
--- /dev/null
+++ b/engines/hugo/parser_v1d.cpp
@@ -0,0 +1,355 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// parser.c - handles all keyboard/command input
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/parser.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+Parser_v1d::Parser_v1d(HugoEngine &vm) : Parser(vm) {
+}
+
+Parser_v1d::~Parser_v1d() {
+}
+
+// Locate word in list of nouns and return ptr to string in noun list
+// If n is NULL, start at beginning of list, else with n
+char *Parser_v1d::findNextNoun(char *noun) {
+ debugC(1, kDebugParser, "findNextNoun(%s)", noun);
+
+ int currNounIndex = -1;
+ if (noun) { // If noun not NULL, find index
+ for (currNounIndex = 0; _vm._arrayNouns[currNounIndex]; currNounIndex++) {
+ if (noun == _vm._arrayNouns[currNounIndex][0])
+ break;
+ }
+ }
+ for (int i = currNounIndex + 1; _vm._arrayNouns[i]; i++) {
+ for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) {
+ if (strstr(_line, _vm._arrayNouns[i][j]))
+ return _vm._arrayNouns[i][0];
+ }
+ }
+ return 0;
+}
+
+// Test whether hero is close to object. Return TRUE or FALSE
+// If no noun specified, check context flag in object before other tests.
+// If object not near, return suitable string; may be similar object closer
+// If radius is -1, treat radius as infinity
+bool Parser_v1d::isNear(char *verb, char *noun, object_t *obj, char *comment) {
+ debugC(1, kDebugParser, "isNear(%s, %s, obj, %s)", verb, noun, comment);
+
+ if (!noun && !obj->verbOnlyFl) { // No noun specified & object not context senesitive
+ return false;
+ } else if (noun && (noun != _vm._arrayNouns[obj->nounIndex][0])) { // Noun specified & not same as object
+ return false;
+ } else if (obj->carriedFl) { // Object is being carried
+ return true;
+ } else if (obj->screenIndex != *_vm._screen_p) { // Not in same screen
+ if (obj->objValue)
+ strcpy (comment, _vm._textParser[kCmtAny4]);
+ return false;
+ }
+
+ if (obj->cycling == INVISIBLE) {
+ if (obj->seqNumb) { // There is an image
+ strcpy(comment, _vm._textParser[kCmtAny5]);
+ return false;
+ } else { // No image, assume visible
+ if ((obj->radius < 0) ||
+ ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
+ (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ return true;
+ } else {
+ // User is either not close enough (stationary, valueless objects)
+ // or is not carrying it (small, portable objects of value)
+ if (noun) { // Don't say unless object specified
+ if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
+ strcpy(comment, _vm._textParser[kCmtAny4]);
+ else
+ strcpy(comment, _vm._textParser[kCmtClose]);
+ }
+ return false;
+ }
+ }
+ }
+
+ if ((obj->radius < 0) ||
+ ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
+ (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ return true;
+ } else {
+ // User is either not close enough (stationary, valueless objects)
+ // or is not carrying it (small, portable objects of value)
+ if (noun) { // Don't say unless object specified
+ if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
+ strcpy(comment, _vm._textParser[kCmtAny4]);
+ else
+ strcpy(comment, _vm._textParser[kCmtClose]);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+// Test whether supplied verb is one of the common variety for this object
+// say_ok needed for special case of take/drop which may be handled not only
+// here but also in a cmd_list with a donestr string simultaneously
+bool Parser_v1d::isGenericVerb(char *word, object_t *obj) {
+ debugC(1, kDebugParser, "isGenericVerb(%s, object_t *obj)", word);
+
+ if (!obj->genericCmd)
+ return false;
+
+ // Following is equivalent to switch, but couldn't do one
+ if (word == _vm._arrayVerbs[_vm._look][0]) {
+ if ((LOOK & obj->genericCmd) == LOOK)
+ Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]);
+ else
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual_1d]);
+ } else if (word == _vm._arrayVerbs[_vm._take][0]) {
+ if (obj->carriedFl)
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]);
+ else if ((TAKE & obj->genericCmd) == TAKE)
+ takeObject(obj);
+ else if (!obj->verbOnlyFl) // Make sure not taking object in context!
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]);
+ else
+ return false;
+ } else if (word == _vm._arrayVerbs[_vm._drop][0]) {
+ if (!obj->carriedFl)
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]);
+ else if ((DROP & obj->genericCmd) == DROP)
+ dropObject(obj);
+ else
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]);
+ } else { // It was not a generic cmd
+ return false;
+ }
+
+ return true;
+}
+
+// Test whether supplied verb is included in the list of allowed verbs for
+// this object. If it is, then perform the tests on it from the cmd list
+// and if it passes, perform the actions in the action list. If the verb
+// is catered for, return TRUE
+bool Parser_v1d::isObjectVerb(char *word, object_t *obj) {
+ debugC(1, kDebugParser, "isObjectVerb(%s, object_t *obj)", word);
+
+ // First, find matching verb in cmd list
+ uint16 cmdIndex = obj->cmdIndex; // ptr to list of commands
+ if (!cmdIndex) // No commands for this obj
+ return false;
+
+ int i;
+ for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
+ if (!strcmp(word, _vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex][0])) // Is this verb catered for?
+ break;
+ }
+
+ if (_vm._cmdList[cmdIndex][i].verbIndex == 0) // No
+ return false;
+
+ // Verb match found, check all required objects are being carried
+ cmd *cmnd = &_vm._cmdList[cmdIndex][i]; // ptr to struct cmd
+ if (cmnd->reqIndex) { // At least 1 thing in list
+ uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
+ for (i = 0; reqs[i]; i++) { // for each obj
+ if (!isCarrying(reqs[i])) {
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]);
+ return true;
+ }
+ }
+ }
+
+ // Required objects are present, now check state is correct
+ if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)){
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]);
+ return true;
+ }
+
+ // Everything checked. Change the state and carry out any actions
+ if (cmnd->reqState != DONT_CARE) // Don't change new state if required state didn't care
+ obj->state = cmnd->newState;
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]);
+ _vm.scheduler().insertActionList(cmnd->actIndex);
+ // Special case if verb is Take or Drop. Assume additional generic actions
+ if ((word == _vm._arrayVerbs[_vm._take][0]) || (word == _vm._arrayVerbs[_vm._drop][0]))
+ isGenericVerb(word, obj);
+ return true;
+}
+
+// Print text for possible background object. Return TRUE if match found
+// Only match if both verb and noun found. Test_ca will match verb-only
+bool Parser_v1d::isBackgroundWord(char *noun, char *verb, objectList_t obj) {
+ debugC(1, kDebugParser, "isBackgroundWord(%s, %s, object_list_t obj)", noun, verb);
+
+ if (!noun)
+ return false;
+
+ for (int i = 0; obj[i].verbIndex; i++) {
+ if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && (noun == _vm._arrayNouns[obj[i].nounIndex][0])) {
+ Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ return true;
+ }
+ }
+ return false;
+}
+
+// Do all things necessary to carry an object
+void Parser_v1d::takeObject(object_t *obj) {
+ debugC(1, kDebugParser, "takeObject(object_t *obj)");
+
+ obj->carriedFl = true;
+ if (obj->seqNumb) // Don't change if no image to display
+ obj->cycling = ALMOST_INVISIBLE;
+
+ _vm.adjustScore(obj->objValue);
+
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[obj->nounIndex][TAKE_NAME]);
+}
+
+// Do all necessary things to drop an object
+void Parser_v1d::dropObject(object_t *obj) {
+ debugC(1, kDebugParser, "dropObject(object_t *obj)");
+
+ obj->carriedFl = false;
+ obj->screenIndex = *_vm._screen_p;
+ if (obj->seqNumb) // Don't change if no image to display
+ obj->cycling = NOT_CYCLING;
+ obj->x = _vm._hero->x - 1;
+ obj->y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
+ _vm.adjustScore(-obj->objValue);
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBOk]);
+}
+
+// Print text for possible background object. Return TRUE if match found
+// If test_noun TRUE, must have a noun given
+bool Parser_v1d::isCatchallVerb(bool testNounFl, char *noun, char *verb, objectList_t obj) {
+ debugC(1, kDebugParser, "isCatchallVerb(%d, %s, %s, object_list_t obj)", (testNounFl) ? 1 : 0, noun, verb);
+
+ if (testNounFl && !noun)
+ return false;
+
+ for (int i = 0; obj[i].verbIndex; i++) {
+ if ((verb == _vm._arrayVerbs[obj[i].verbIndex][0]) && ((noun == _vm._arrayNouns[obj[i].nounIndex][0]) || (obj[i].nounIndex == 0))) {
+ Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ return true;
+ }
+ }
+ return false;
+}
+
+// Parse the user's line of text input. Generate events as necessary
+void Parser_v1d::lineHandler() {
+ debugC(1, kDebugParser, "lineHandler()");
+
+ object_t *obj;
+ status_t &gameStatus = _vm.getGameStatus();
+ char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
+
+// Reset_prompt_line ();
+ Utils::strlwr(_line); // Convert to lower case
+
+ if (!strcmp("exit", _line) || strstr(_line, "quit")) {
+ if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
+ _vm.endGame();
+ return;
+ }
+
+ // SAVE/RESTORE
+ if (!strcmp("save", _line)) {
+ if (gameStatus.gameOverFl)
+ Utils::gameOverMsg();
+ else
+// _vm.file().saveOrRestore(true);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ if (!strcmp("restore", _line)) {
+// _vm.file().saveOrRestore(false);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ if (*_line == '\0') // Empty line
+ return;
+
+ if (strspn(_line, " ") == strlen(_line)) // Nothing but spaces!
+ return;
+
+ if (gameStatus.gameOverFl) { // No commands allowed!
+ Utils::gameOverMsg();
+ return;
+ }
+
+ // Find the first verb in the line
+ char *verb = findVerb();
+ char *noun = 0; // Noun not found yet
+
+ if (verb) { // OK, verb found. Try to match with object
+ do {
+ noun = findNextNoun(noun); // Find a noun in the line
+ // Must try at least once for objects allowing verb-context
+ for (int i = 0; i < _vm._numObj; i++) {
+ obj = &_vm._objects[i];
+ if (isNear(verb, noun, obj, farComment)) {
+ if (isObjectVerb(verb, obj) // Foreground object
+ || isGenericVerb(verb, obj)) // Common action type
+ return;
+ }
+ }
+ if ((*farComment == '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ } while (noun);
+ }
+ noun = findNextNoun(noun);
+ if (*farComment != '\0') // An object matched but not near enough
+ Utils::Box(BOX_ANY, "%s", farComment);
+ else if (!isCatchallVerb(true, noun, verb, _vm._catchallList) &&
+ !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p]) &&
+ !isCatchallVerb(false, noun, verb, _vm._catchallList))
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh_1d]);
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser_v1w.cpp b/engines/hugo/parser_v1w.cpp
new file mode 100644
index 0000000000..417b31e794
--- /dev/null
+++ b/engines/hugo/parser_v1w.cpp
@@ -0,0 +1,434 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// parser.c - handles all keyboard/command input
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/parser.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+Parser_v1w::Parser_v1w(HugoEngine &vm) : Parser(vm) {
+}
+
+Parser_v1w::~Parser_v1w() {
+}
+
+// Test whether command line contains a verb allowed by this object.
+// If it does, and the object is near and passes the tests in the command
+// list then carry out the actions in the action list and return TRUE
+bool Parser_v1w::isObjectVerb(object_t *obj, char *comment) {
+ debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s)", comment);
+
+ // First, find matching verb in cmd list
+ uint16 cmdIndex = obj->cmdIndex; // ptr to list of commands
+ if (cmdIndex == 0) // No commands for this obj
+ return false;
+
+ int i;
+ for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++) { // For each cmd
+ if (isWordPresent(_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex])) // Was this verb used?
+ break;
+ }
+
+ if (_vm._cmdList[cmdIndex][i].verbIndex == 0) // No verbs used.
+ return false;
+
+ // Verb match found. Check if object is Near
+ char *verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex];
+ if (!isNear(obj, verb, comment))
+ return false;
+
+ // Check all required objects are being carried
+ cmd *cmnd = &_vm._cmdList[cmdIndex][i]; // ptr to struct cmd
+ if (cmnd->reqIndex) { // At least 1 thing in list
+ uint16 *reqs = _vm._arrayReqs[cmnd->reqIndex]; // ptr to list of required objects
+ for (i = 0; reqs[i]; i++) { // for each obj
+ if (!isCarrying(reqs[i])) {
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataNoCarryIndex]);
+ return true;
+ }
+ }
+ }
+
+ // Required objects are present, now check state is correct
+ if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)) {
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataWrongIndex]);
+ return true;
+ }
+
+ // Everything checked. Change the state and carry out any actions
+ if (cmnd->reqState != DONT_CARE) // Don't change new state if required state didn't care
+ obj->state = cmnd->newState;
+ Utils::Box(BOX_ANY, "%s", _vm._textData[cmnd->textDataDoneIndex]);
+ _vm.scheduler().insertActionList(cmnd->actIndex);
+
+ // See if any additional generic actions
+ if ((verb == _vm._arrayVerbs[_vm._look][0]) || (verb == _vm._arrayVerbs[_vm._take][0]) || (verb == _vm._arrayVerbs[_vm._drop][0]))
+ isGenericVerb(obj, comment);
+ return true;
+}
+
+// Test whether command line contains one of the generic actions
+bool Parser_v1w::isGenericVerb(object_t *obj, char *comment) {
+ debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s)", comment);
+
+ if (!obj->genericCmd)
+ return false;
+
+ // Following is equivalent to switch, but couldn't do one
+ if (isWordPresent(_vm._arrayVerbs[_vm._look]) && isNear(obj, _vm._arrayVerbs[_vm._look][0], comment)) {
+ // Test state-dependent look before general look
+ if ((obj->genericCmd & LOOK_S) == LOOK_S) {
+ Utils::Box(BOX_ANY, "%s", _vm._textData[obj->stateDataIndex[obj->state]]);
+ warning("isGenericVerb: use of state dependant look - To be validated");
+ } else {
+ if ((LOOK & obj->genericCmd) == LOOK) {
+ if (_vm._textData[obj->dataIndex])
+ Utils::Box(BOX_ANY, "%s", _vm._textData[obj->dataIndex]);
+ else
+ return false;
+ } else {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBUnusual]);
+ }
+ }
+ } else if (isWordPresent(_vm._arrayVerbs[_vm._take]) && isNear(obj, _vm._arrayVerbs[_vm._take][0], comment)) {
+ if (obj->carriedFl)
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBHave]);
+ else if ((TAKE & obj->genericCmd) == TAKE)
+ takeObject(obj);
+ else if (obj->cmdIndex != 0) // No comment if possible commands
+ return false;
+ else if (!obj->verbOnlyFl && (TAKE & obj->genericCmd) == TAKE) // Make sure not taking object in context!
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse]);
+ else
+ return false;
+ } else if (isWordPresent(_vm._arrayVerbs[_vm._drop])) {
+ if (!obj->carriedFl && ((DROP & obj->genericCmd) == DROP))
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBDontHave]);
+ else if (obj->carriedFl && ((DROP & obj->genericCmd) == DROP))
+ dropObject(obj);
+ else if (obj->cmdIndex == 0)
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNeed]);
+ else
+ return false;
+ } else { // It was not a generic cmd
+ return false;
+ }
+
+ return true;
+}
+
+// Test whether hero is close to object. Return TRUE or FALSE
+// If object not near, return suitable comment; may be another object close
+// If radius is -1, treat radius as infinity
+// Verb is included to determine correct comment if not near
+bool Parser_v1w::isNear(object_t *obj, char *verb, char *comment) {
+ debugC(1, kDebugParser, "isNear(object_t *obj, %s, %s)", verb, comment);
+
+ if (obj->carriedFl) // Object is being carried
+ return true;
+
+ if (obj->screenIndex != *_vm._screen_p) {
+ // Not in same screen
+ if (obj->objValue)
+ strcpy(comment, _vm._textParser[kCmtAny1]);
+ else
+ strcpy(comment, _vm._textParser[kCmtAny2]);
+ return false;
+ }
+
+ if (obj->cycling == INVISIBLE) {
+ if (obj->seqNumb) {
+ // There is an image
+ strcpy(comment, _vm._textParser[kCmtAny3]);
+ return false;
+ } else {
+ // No image, assume visible
+ if ((obj->radius < 0) ||
+ ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
+ (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ return true;
+ } else {
+ // User is not close enough
+ if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
+ strcpy(comment, _vm._textParser[kCmtAny1]);
+ else
+ strcpy(comment, _vm._textParser[kCmtClose]);
+ return false;
+ }
+ }
+ }
+
+ if ((obj->radius < 0) ||
+ ((abs(obj->x - _vm._hero->x) <= obj->radius) &&
+ (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) {
+ return true;
+ } else {
+ // User is not close enough
+ if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0]))
+ strcpy(comment, _vm._textParser[kCmtAny1]);
+ else
+ strcpy(comment, _vm._textParser[kCmtClose]);
+ return false;
+ }
+ return true;
+}
+
+// Do all things necessary to carry an object
+void Parser_v1w::takeObject(object_t *obj) {
+ debugC(1, kDebugParser, "takeObject(object_t *obj)");
+
+ obj->carriedFl = true;
+ if (obj->seqNumb) { // Don't change if no image to display
+ obj->cycling = INVISIBLE;
+ }
+ _vm.adjustScore(obj->objValue);
+
+ if (obj->seqNumb > 0) // If object has an image, force walk to dropped
+ obj->viewx = -1; // (possibly moved) object next time taken!
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[obj->nounIndex][TAKE_NAME]);
+}
+
+// Do all necessary things to drop an object
+void Parser_v1w::dropObject(object_t *obj) {
+ debugC(1, kDebugParser, "dropObject(object_t *obj)");
+
+ obj->carriedFl = false;
+ obj->screenIndex = *_vm._screen_p;
+ if ((obj->seqNumb > 1) || (obj->seqList[0].imageNbr > 1))
+ obj->cycling = CYCLE_FORWARD;
+ else
+ obj->cycling = NOT_CYCLING;
+ obj->x = _vm._hero->x - 1;
+ obj->y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
+ obj->y = (obj->y + obj->currImagePtr->y2 < YPIX) ? obj->y : YPIX - obj->currImagePtr->y2 - 10;
+ _vm.adjustScore(-obj->objValue);
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBOk]);
+}
+
+// Search for matching verbs in background command list.
+// Noun is not required. Return TRUE if match found
+// Note that if the background command list has match set TRUE then do not
+// print text if there are any recognizable nouns in the command line
+bool Parser_v1w::isCatchallVerb(objectList_t obj) {
+ debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj)");
+
+ for (int i = 0; obj[i].verbIndex != 0; i++) {
+ if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 &&
+ (!obj[i].matchFl || !findNoun()) &&
+ ((obj[i].roomState == DONT_CARE) ||
+ (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) {
+ Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ _vm.scheduler().processBonus(obj[i].bonusIndex);
+
+ // If this is LOOK (without a noun), show any takeable objects
+ if (*(_vm._arrayVerbs[obj[i].verbIndex]) == _vm._arrayVerbs[_vm._look][0])
+ showTakeables();
+
+ return true;
+ }
+ }
+ return false;
+}
+
+// Search for matching verb/noun pairs in background command list
+// Print text for possible background object. Return TRUE if match found
+bool Parser_v1w::isBackgroundWord(objectList_t obj) {
+ debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj)");
+
+ for (int i = 0; obj[i].verbIndex != 0; i++) {
+ if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) &&
+ isWordPresent(_vm._arrayNouns[obj[i].nounIndex]) &&
+ ((obj[i].roomState == DONT_CARE) ||
+ (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) {
+ Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(obj[i].commentIndex));
+ _vm.scheduler().processBonus(obj[i].bonusIndex);
+ return true;
+ }
+ }
+ return false;
+}
+
+// Parse the user's line of text input. Generate events as necessary
+void Parser_v1w::lineHandler() {
+ debugC(1, kDebugParser, "lineHandler()");
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ // Toggle God Mode
+ if (!strncmp(_line, "PPG", 3)) {
+ _vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI);
+ gameStatus.godModeFl ^= 1;
+ return;
+ }
+
+ Utils::strlwr(_line); // Convert to lower case
+
+ // God Mode cheat commands:
+ // goto <screen> Takes hero to named screen
+ // fetch <object name> Hero carries named object
+ // fetch all Hero carries all possible objects
+ // find <object name> Takes hero to screen containing named object
+ if (gameStatus.godModeFl) {
+ // Special code to allow me to go straight to any screen
+ if (strstr(_line, "goto")) {
+ for (int i = 0; i < _vm._numScreens; i++) {
+ if (!strcmp(&_line[strlen("goto") + 1], _vm._screenNames[i])) {
+ _vm.scheduler().newScreen(i);
+ return;
+ }
+ }
+ }
+
+ // Special code to allow me to get objects from anywhere
+ if (strstr(_line, "fetch all")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (_vm._objects[i].genericCmd & TAKE)
+ takeObject(&_vm._objects[i]);
+ }
+ return;
+ }
+
+ if (strstr(_line, "fetch")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
+ takeObject(&_vm._objects[i]);
+ return;
+ }
+ }
+ }
+
+ // Special code to allow me to goto objects
+ if (strstr(_line, "find")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (!strcmp(&_line[strlen("find") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
+ _vm.scheduler().newScreen(_vm._objects[i].screenIndex);
+ return;
+ }
+ }
+ }
+ }
+
+ // Special meta commands
+ // EXIT/QUIT
+ if (!strcmp("exit", _line) || strstr(_line, "quit")) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBExit]);
+ return;
+ }
+
+ // SAVE/RESTORE
+ if (!strcmp("save", _line) && gameStatus.viewState == V_PLAY) {
+ _vm.file().saveGame(gameStatus.saveSlot, "Current game");
+ return;
+ }
+
+ if (!strcmp("restore", _line) && (gameStatus.viewState == V_PLAY || gameStatus.viewState == V_IDLE)) {
+ _vm.file().restoreGame(gameStatus.saveSlot);
+ _vm.scheduler().restoreScreen(*_vm._screen_p);
+ gameStatus.viewState = V_PLAY;
+ return;
+ }
+
+ // Empty line
+ if (*_line == '\0') // Empty line
+ return;
+ if (strspn(_line, " ") == strlen(_line)) // Nothing but spaces!
+ return;
+
+ if (gameStatus.gameOverFl) {
+ // No commands allowed!
+ Utils::gameOverMsg();
+ return;
+ }
+
+ char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
+
+ // Test for nearby objects referenced explicitly
+ for (int i = 0; i < _vm._numObj; i++) {
+ object_t *obj = &_vm._objects[i];
+ if (isWordPresent(_vm._arrayNouns[obj->nounIndex])) {
+ if (isObjectVerb(obj, farComment) || isGenericVerb(obj, farComment))
+ return;
+ }
+ }
+
+ // Test for nearby objects that only require a verb
+ // Note comment is unused if not near.
+ for (int i = 0; i < _vm._numObj; i++) {
+ object_t *obj = &_vm._objects[i];
+ if (obj->verbOnlyFl) {
+ char contextComment[XBYTES * 5] = ""; // Unused comment for context objects
+ if (isObjectVerb(obj, contextComment) || isGenericVerb(obj, contextComment))
+ return;
+ }
+ }
+
+ // No objects match command line, try background and catchall commands
+ if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ if (isBackgroundWord(_vm._catchallList))
+ return;
+ if (isCatchallVerb(_vm._catchallList))
+ return;
+
+ // If a not-near comment was generated, print it
+ if (*farComment != '\0') {
+ Utils::Box(BOX_ANY, "%s", farComment);
+ return;
+ }
+
+ // Nothing matches. Report recognition success to user.
+ char *verb = findVerb();
+ char *noun = findNoun();
+ if (verb == _vm._arrayVerbs[_vm._look][0] && _maze.enabledFl) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBMaze]);
+ showTakeables();
+ } else if (verb && noun) { // A combination I didn't think of
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoPoint]);
+ } else if (noun) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ } else if (verb) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBVerb]);
+ } else {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh]);
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser_v2d.cpp b/engines/hugo/parser_v2d.cpp
new file mode 100644
index 0000000000..adaf5e9f9f
--- /dev/null
+++ b/engines/hugo/parser_v2d.cpp
@@ -0,0 +1,137 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// parser.c - handles all keyboard/command input
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+Parser_v2d::Parser_v2d(HugoEngine &vm) : Parser_v1d(vm) {
+}
+
+Parser_v2d::~Parser_v2d() {
+}
+
+// Parse the user's line of text input. Generate events as necessary
+void Parser_v2d::lineHandler() {
+ debugC(1, kDebugParser, "lineHandler()");
+
+ object_t *obj;
+ status_t &gameStatus = _vm.getGameStatus();
+ char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
+
+// Reset_prompt_line ();
+ Utils::strlwr(_line); // Convert to lower case
+
+ if (!strcmp("exit", _line) || strstr(_line, "quit")) {
+ if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
+ _vm.endGame();
+ else
+ return;
+ }
+
+ // SAVE/RESTORE
+ if (!strcmp("save", _line)) {
+ _config.soundFl = false;
+ if (gameStatus.gameOverFl)
+ Utils::gameOverMsg();
+ else
+// _vm.file().saveOrRestore(true);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ if (!strcmp("restore", _line)) {
+ _config.soundFl = false;
+// _vm.file().saveOrRestore(false);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ if (*_line == '\0') // Empty line
+ return;
+
+ if (strspn(_line, " ") == strlen(_line)) // Nothing but spaces!
+ return;
+
+ if (gameStatus.gameOverFl) { // No commands allowed!
+ Utils::gameOverMsg();
+ return;
+ }
+
+ // Find the first verb in the line
+ char *verb = findVerb();
+ char *noun = 0; // Noun not found yet
+
+ if (verb) { // OK, verb found. Try to match with object
+ do {
+ noun = findNextNoun(noun); // Find a noun in the line
+ // Must try at least once for objects allowing verb-context
+ for (int i = 0; i < _vm._numObj; i++) {
+ obj = &_vm._objects[i];
+ if (isNear(verb, noun, obj, farComment)) {
+ if (isObjectVerb(verb, obj) // Foreground object
+ || isGenericVerb(verb, obj)) // Common action type
+ return;
+ }
+ }
+ if ((*farComment != '\0') && isBackgroundWord(noun, verb, _vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ } while (noun);
+ }
+
+ noun = findNextNoun(noun);
+ if ( !isCatchallVerb(true, noun, verb, _vm._backgroundObjects[*_vm._screen_p])
+ && !isCatchallVerb(true, noun, verb, _vm._catchallList)
+ && !isCatchallVerb(false, noun, verb, _vm._backgroundObjects[*_vm._screen_p])
+ && !isCatchallVerb(false, noun, verb, _vm._catchallList)) {
+ if (*farComment != '\0') { // An object matched but not near enough
+ Utils::Box(BOX_ANY, "%s", farComment);
+ } else if (_maze.enabledFl && (verb == _vm._arrayVerbs[_vm._look][0])) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBMaze]);
+ showTakeables();
+ } else if (verb && noun) { // A combination I didn't think of
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoUse_2d]);
+ } else if (verb || noun) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ } else {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh_2d]);
+ }
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/parser_v3d.cpp b/engines/hugo/parser_v3d.cpp
new file mode 100644
index 0000000000..501d8fba20
--- /dev/null
+++ b/engines/hugo/parser_v3d.cpp
@@ -0,0 +1,203 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// parser.c - handles all keyboard/command input
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/parser.h"
+#include "hugo/schedule.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+Parser_v3d::Parser_v3d(HugoEngine &vm) : Parser_v1w(vm) {
+}
+
+Parser_v3d::~Parser_v3d() {
+}
+
+// Parse the user's line of text input. Generate events as necessary
+void Parser_v3d::lineHandler() {
+ debugC(1, kDebugParser, "lineHandler()");
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ // Toggle God Mode
+ if (!strncmp(_line, "PPG", 3)) {
+ _vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI);
+ gameStatus.godModeFl ^= 1;
+ return;
+ }
+
+ Utils::strlwr(_line); // Convert to lower case
+
+ // God Mode cheat commands:
+ // goto <screen> Takes hero to named screen
+ // fetch <object name> Hero carries named object
+ // fetch all Hero carries all possible objects
+ // find <object name> Takes hero to screen containing named object
+ if (gameStatus.godModeFl) {
+ // Special code to allow me to go straight to any screen
+ if (strstr(_line, "goto")) {
+ for (int i = 0; i < _vm._numScreens; i++) {
+ if (!strcmp(&_line[strlen("goto") + 1], _vm._screenNames[i])) {
+ _vm.scheduler().newScreen(i);
+ return;
+ }
+ }
+ }
+
+ // Special code to allow me to get objects from anywhere
+ if (strstr(_line, "fetch all")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (_vm._objects[i].genericCmd & TAKE)
+ takeObject(&_vm._objects[i]);
+ }
+ return;
+ }
+
+ if (strstr(_line, "fetch")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (!strcmp(&_line[strlen("fetch") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
+ takeObject(&_vm._objects[i]);
+ return;
+ }
+ }
+ }
+
+ // Special code to allow me to goto objects
+ if (strstr(_line, "find")) {
+ for (int i = 0; i < _vm._numObj; i++) {
+ if (!strcmp(&_line[strlen("find") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) {
+ _vm.scheduler().newScreen(_vm._objects[i].screenIndex);
+ return;
+ }
+ }
+ }
+ }
+
+ // Special meta commands
+ // EXIT/QUIT
+ if (!strcmp("exit", _line) || strstr(_line, "quit")) {
+ if (Utils::Box(BOX_YESNO, "%s", _vm._textParser[kTBExit_1d]) != 0)
+ _vm.endGame();
+ else
+ return;
+ }
+
+ // SAVE/RESTORE
+ if (!strcmp("save", _line)) {
+ _config.soundFl = false;
+ if (gameStatus.gameOverFl)
+ Utils::gameOverMsg();
+ else
+// _vm.file().saveOrRestore(true);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ if (!strcmp("restore", _line)) {
+ _config.soundFl = false;
+// _vm.file().saveOrRestore(false);
+ warning("STUB: saveOrRestore()");
+ return;
+ }
+
+ // Empty line
+ if (*_line == '\0') // Empty line
+ return;
+ if (strspn(_line, " ") == strlen(_line)) // Nothing but spaces!
+ return;
+
+ if (gameStatus.gameOverFl) {
+ // No commands allowed!
+ Utils::gameOverMsg();
+ return;
+ }
+
+ char farComment[XBYTES * 5] = ""; // hold 5 line comment if object not nearby
+
+ // Test for nearby objects referenced explicitly
+ for (int i = 0; i < _vm._numObj; i++) {
+ object_t *obj = &_vm._objects[i];
+ if (isWordPresent(_vm._arrayNouns[obj->nounIndex])) {
+ if (isObjectVerb(obj, farComment) || isGenericVerb(obj, farComment))
+ return;
+ }
+ }
+
+ // Test for nearby objects that only require a verb
+ // Note comment is unused if not near.
+ for (int i = 0; i < _vm._numObj; i++) {
+ object_t *obj = &_vm._objects[i];
+ if (obj->verbOnlyFl) {
+ char contextComment[XBYTES * 5] = ""; // Unused comment for context objects
+ if (isObjectVerb(obj, contextComment) || isGenericVerb(obj, contextComment))
+ return;
+ }
+ }
+
+ // No objects match command line, try background and catchall commands
+ if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p]))
+ return;
+ if (isBackgroundWord(_vm._catchallList))
+ return;
+ if (isCatchallVerb(_vm._catchallList))
+ return;
+
+ // If a not-near comment was generated, print it
+ if (*farComment != '\0') {
+ Utils::Box(BOX_ANY, "%s", farComment);
+ return;
+ }
+
+ // Nothing matches. Report recognition success to user.
+ char *verb = findVerb();
+ char *noun = findNoun();
+
+ if (verb && noun) { // A combination I didn't think of
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoPoint]);
+ } else if (noun) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBNoun]);
+ } else if (verb) {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBVerb]);
+ } else {
+ Utils::Box(BOX_ANY, "%s", _vm._textParser[kTBEh]);
+ }
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/route.cpp b/engines/hugo/route.cpp
new file mode 100644
index 0000000000..2ba95fb7d7
--- /dev/null
+++ b/engines/hugo/route.cpp
@@ -0,0 +1,487 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// Find shortest route from hero to destination
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/game.h"
+#include "hugo/route.h"
+#include "hugo/global.h"
+
+namespace Hugo {
+Route::Route(HugoEngine &vm) : _vm(vm) {
+}
+
+// Face hero in new direction, based on cursor key input by user.
+void Route::setDirection(uint16 keyCode) {
+ debugC(1, kDebugRoute, "setDirection(%d)", keyCode);
+
+ object_t *obj = _vm._hero; // Pointer to hero object
+
+ // Set first image in sequence
+ switch (keyCode) {
+ case Common::KEYCODE_UP:
+ obj->currImagePtr = obj->seqList[_UP].seqPtr;
+ break;
+ case Common::KEYCODE_DOWN:
+ obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+ break;
+ case Common::KEYCODE_LEFT:
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ break;
+ case Common::KEYCODE_RIGHT:
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ break;
+ case Common::KEYCODE_HOME:
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ break;
+ case Common::KEYCODE_END:
+ obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+ break;
+ case Common::KEYCODE_PAGEUP:
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ break;
+ case Common::KEYCODE_PAGEDOWN:
+ obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+ break;
+ }
+}
+
+// Set hero walking, based on cursor key input by user.
+// Hitting same key twice will stop hero.
+void Route::setWalk(uint16 direction) {
+ debugC(1, kDebugRoute, "setWalk(%d)", direction);
+
+ static uint16 oldDirection = 0; // Last direction char
+ object_t *obj = _vm._hero; // Pointer to hero object
+
+ if (_vm.getGameStatus().storyModeFl || obj->pathType != USER) // Make sure user has control
+ return;
+
+ if (!obj->vx && !obj->vy)
+ oldDirection = 0; // Fix for consistant restarts
+
+ if (direction != oldDirection) {
+ // Direction has changed
+ setDirection(direction); // Face new direction
+ obj->vx = obj->vy = 0;
+ switch (direction) { // And set correct velocity
+ case Common::KEYCODE_UP:
+ obj->vy = -DY;
+ break;
+ case Common::KEYCODE_DOWN:
+ obj->vy = DY;
+ break;
+ case Common::KEYCODE_LEFT:
+ obj->vx = -DX;
+ break;
+ case Common::KEYCODE_RIGHT:
+ obj->vx = DX;
+ break;
+ case Common::KEYCODE_HOME:
+ obj->vx = -DX;
+ // Note: in v1 Dos and v2 Dos, obj->vy is set to DY
+ obj->vy = -DY / 2;
+ break;
+ case Common::KEYCODE_END:
+ obj->vx = -DX;
+ // Note: in v1 Dos and v2 Dos, obj->vy is set to -DY
+ obj->vy = DY / 2;
+ break;
+ case Common::KEYCODE_PAGEUP:
+ obj->vx = DX;
+ // Note: in v1 Dos and v2 Dos, obj->vy is set to -DY
+ obj->vy = -DY / 2;
+ break;
+ case Common::KEYCODE_PAGEDOWN:
+ obj->vx = DX;
+ // Note: in v1 Dos and v2 Dos, obj->vy is set to DY
+ obj->vy = DY / 2;
+ break;
+ }
+ oldDirection = direction;
+ obj->cycling = CYCLE_FORWARD;
+ } else {
+ // Same key twice - halt hero
+ obj->vy = 0;
+ obj->vx = 0;
+ oldDirection = 0;
+ obj->cycling = NOT_CYCLING;
+ }
+}
+
+// Recursive algorithm! Searches from hero to dest_x, dest_y
+// Find horizontal line segment about supplied point and recursively
+// find line segments for each point above and below that segment.
+// When destination point found in segment, start surfacing and leave
+// a trail in segment[] from destination back to hero.
+//
+// Note: there is a bug which allows a route through a 1-pixel high
+// narrow gap if between 2 segments wide enough for hero. To work
+// around this, make sure any narrow gaps are 2 or more pixels high.
+// An example of this was the blocking guard in Hugo1/Dead-End.
+void Route::segment(int16 x, int16 y) {
+ debugC(1, kDebugRoute, "segment(%d, %d)", x, y);
+
+// Note use of static - can't waste stack
+ static image_pt p; // Ptr to _boundaryMap[y]
+ static segment_t *seg_p; // Ptr to segment
+
+ // Bomb out if stack exhausted
+ // Vinterstum: Is this just a safeguard, or actually used?
+ //_fullStackFl = _stackavail () < 256;
+ _fullStackFl = false;
+
+ // Find and fill on either side of point
+ p = _boundaryMap[y];
+ int16 x1, x2; // Range of segment
+ for (x1 = x; x1 > 0; x1--) {
+ if (p[x1] == 0) {
+ p[x1] = kMapFill;
+ } else {
+ break;
+ }
+ }
+ for (x2 = x + 1; x2 < XPIX; x2++) {
+ if (p[x2] == 0) {
+ p[x2] = kMapFill;
+ } else {
+ break;
+ }
+ }
+ x1++;
+ x2--;
+
+ // Discard path if not wide enough for hero - dead end
+ if (_heroWidth > x2 - x1 + 1)
+ return;
+
+ // Have we found the destination yet?
+ if (y == _destY && x1 <= _destX && x2 >= _destX)
+ _routeFoundFl = true;
+
+ // Bounds check y in case no boundary around screen
+ if (y <= 0 || y >= YPIX - 1)
+ return;
+
+ if (_vm._hero->x < x1) {
+ // Hero x not in segment, search x1..x2
+ // Find all segments above current
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ if (_boundaryMap[y - 1][x] == 0)
+ segment(x, y - 1);
+ }
+
+ // Find all segments below current
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ if (_boundaryMap[y + 1][x] == 0)
+ segment(x, y + 1);
+ }
+ } else if (_vm._hero->x + HERO_MAX_WIDTH > x2) {
+ // Hero x not in segment, search x1..x2
+ // Find all segments above current
+ for (x = x2; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x >= x1; x--) {
+ if (_boundaryMap[y - 1][x] == 0)
+ segment(x, y - 1);
+ }
+
+ // Find all segments below current
+ for (x = x2; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x >= x1; x--) {
+ if (_boundaryMap[y + 1][x] == 0)
+ segment(x, y + 1);
+ }
+ } else {
+ // Organize search around hero x position - this gives
+ // better chance for more direct route.
+ for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ if (_boundaryMap[y - 1][x] == 0)
+ segment(x, y - 1);
+ }
+
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) {
+ if (_boundaryMap[y - 1][x] == 0)
+ segment(x, y - 1);
+ }
+
+ for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) {
+ if (_boundaryMap[y + 1][x] == 0)
+ segment(x, y + 1);
+ }
+
+ for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) {
+ if (_boundaryMap[y + 1][x] == 0)
+ segment(x, y + 1);
+ }
+ }
+
+ // If found, surface, leaving trail back to hero
+ if (_routeFoundFl) {
+ // Bomb out if too many segments (leave one spare)
+ if (_segmentNumb >= kMaxSeg - 1) {
+ _fullSegmentFl = true;
+ } else {
+ // Create segment
+ seg_p = &_segment[_segmentNumb];
+ seg_p->y = y;
+ seg_p->x1 = x1;
+ seg_p->x2 = x2;
+ _segmentNumb++;
+ }
+ }
+}
+
+// Create and return ptr to new node. Initialize with previous node.
+// Returns 0 if MAX_NODES exceeded
+Point *Route::newNode() {
+ debugC(1, kDebugRoute, "newNode");
+
+ if (_routeListIndex >= kMaxNodes) // Too many nodes
+ return 0; // Incomplete route - failure
+ _routeListIndex++;
+ _route[_routeListIndex] = _route[_routeListIndex - 1]; // Initialize with previous node
+ return &_route[_routeListIndex];
+}
+
+// Construct route to cx, cy. Return TRUE if successful.
+// 1. Copy boundary bitmap to local byte map (include object bases)
+// 2. Construct list of segments segment[] from hero to destination
+// 3. Compress to shortest route in route[]
+bool Route::findRoute(int16 cx, int16 cy) {
+ debugC(1, kDebugRoute, "findRoute(%d, %d)", cx, cy);
+
+ // Initialize for search
+ _routeFoundFl = false; // Path not found yet
+ _fullStackFl = false; // Stack not exhausted
+ _fullSegmentFl = false; // Segments not exhausted
+ _segmentNumb = 0; // Segment index
+ _heroWidth = HERO_MIN_WIDTH; // Minimum width of hero
+ _destY = cy; // Destination coords
+ _destX = cx; // Destination coords
+
+ int16 herox1 = _vm._hero->x + _vm._hero->currImagePtr->x1; // Hero baseline
+ int16 herox2 = _vm._hero->x + _vm._hero->currImagePtr->x2; // Hero baseline
+ int16 heroy = _vm._hero->y + _vm._hero->currImagePtr->y2; // Hero baseline
+
+ // Store all object baselines into objbound (except hero's = [0])
+ object_t *obj; // Ptr to object
+ int i;
+ for (i = 1, obj = &_vm._objects[i]; i < _vm._numObj; i++, obj++) {
+ if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
+ _vm.storeBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
+ }
+
+ // Combine objbound and boundary bitmaps to local byte map
+ for (int16 y = 0; y < YPIX; y++) {
+ for (int16 x = 0; x < XBYTES; x++) {
+ for (i = 0; i < 8; i++)
+ _boundaryMap[y][x * 8 + i] = ((_vm.getObjectBoundaryOverlay()[y * XBYTES + x] | _vm.getBoundaryOverlay()[y * XBYTES + x]) & (0x80 >> i)) ? kMapBound : 0;
+ }
+ }
+
+ // Clear all object baselines from objbound
+ for (i = 0, obj = _vm._objects; i < _vm._numObj; i++, obj++) {
+ if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING))
+ _vm.clearBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2);
+ }
+
+ // Search from hero to destination
+ segment(herox1, heroy);
+
+ // Not found or not enough stack or MAX_SEG exceeded
+ if (!_routeFoundFl || _fullStackFl || _fullSegmentFl) {
+ return false;
+ }
+
+ // Now find the route of nodes from destination back to hero
+ // Assign first node as destination
+ _route[0].x = _destX;
+ _route[0].y = _destY;
+
+ // Make a final segment for hero's base (we left a spare)
+ _segment[_segmentNumb].y = heroy;
+ _segment[_segmentNumb].x1 = herox1;
+ _segment[_segmentNumb].x2 = herox2;
+ _segmentNumb++;
+
+ Point *routeNode; // Ptr to route node
+ // Look in segments[] for straight lines from destination to hero
+ for (i = 0, _routeListIndex = 0; i < _segmentNumb - 1; i++) {
+ if ((routeNode = newNode()) == 0) // New node for new segment
+ return false; // Too many nodes
+ routeNode->y = _segment[i].y;
+
+ // Look ahead for furthest straight line
+ for (int16 j = i + 1; j < _segmentNumb; j++) {
+ segment_t *seg_p = &_segment[j];
+ // Can we get to this segment from previous node?
+ if (seg_p->x1 <= routeNode->x && seg_p->x2 >= routeNode->x + _heroWidth - 1) {
+ routeNode->y = seg_p->y; // Yes, keep updating node
+ } else {
+ // No, create another node on previous segment to reach it
+ if ((routeNode = newNode()) == 0) // Add new route node
+ return false; // Too many nodes
+
+ // Find overlap between old and new segments
+ int16 x1 = MAX(_segment[j - 1].x1, seg_p->x1);
+ int16 x2 = MIN(_segment[j - 1].x2, seg_p->x2);
+
+ // If room, add a little offset to reduce staircase effect
+ int16 dx = HERO_MAX_WIDTH >> 1;
+ if (x2 - x1 < _heroWidth + dx)
+ dx = 0;
+
+ // Bear toward final hero position
+ if (j == _segmentNumb - 1)
+ routeNode->x = herox1;
+ else if (herox1 < x1)
+ routeNode->x = x1 + dx;
+ else if (herox1 > x2 - _heroWidth + 1)
+ routeNode->x = x2 - _heroWidth - dx;
+ else
+ routeNode->x = herox1;
+ i = j - 2; // Restart segment (-1 to offset auto increment)
+ break;
+ }
+ }
+
+ // Terminate loop if we've reached hero
+ if (routeNode->x == herox1 && routeNode->y == heroy)
+ break;
+ }
+ return true;
+}
+
+// Process hero in route mode - called from Move_objects()
+void Route::processRoute() {
+ debugC(1, kDebugRoute, "processRoute");
+
+ static bool turnedFl = false; // Used to get extra cylce for turning
+
+ // Current hero position
+ int16 herox = _vm._hero->x + _vm._hero->currImagePtr->x1;
+ int16 heroy = _vm._hero->y + _vm._hero->currImagePtr->y2;
+ status_t &gameStatus = _vm.getGameStatus();
+ Point *routeNode = &_route[gameStatus.routeIndex];
+
+ // Arrived at node?
+ if (abs(herox - routeNode->x) < DX + 1 && abs(heroy - routeNode->y) < DY) {
+ // DX too low
+ // Close enough - position hero exactly
+ _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ _vm._hero->y = _vm._hero->oldy = routeNode->y - _vm._hero->currImagePtr->y2;
+ _vm._hero->vx = _vm._hero->vy = 0;
+ _vm._hero->cycling = NOT_CYCLING;
+
+ // Arrived at final node?
+ if (--gameStatus.routeIndex < 0) {
+ // See why we walked here
+ switch (gameStatus.go_for) {
+ case GO_EXIT: // Walked to an exit, proceed into it
+ setWalk(_vm._hotspots[gameStatus.go_id].direction);
+ break;
+ case GO_LOOK: // Look at an object
+ if (turnedFl) {
+ _vm.lookObject(&_vm._objects[gameStatus.go_id]);
+ turnedFl = false;
+ } else {
+ setDirection(_vm._objects[gameStatus.go_id].direction);
+ gameStatus.routeIndex++; // Come round again
+ turnedFl = true;
+ }
+ break;
+ case GO_GET: // Get (or use) an object
+ if (turnedFl) {
+ _vm.useObject(gameStatus.go_id);
+ turnedFl = false;
+ } else {
+ setDirection(_vm._objects[gameStatus.go_id].direction);
+ gameStatus.routeIndex++; // Come round again
+ turnedFl = true;
+ }
+ break;
+ case GO_SPACE:
+ warning("Unhandled gameStatus.go_for GO_STATUS");
+ break;
+ }
+ }
+ } else if (_vm._hero->vx == 0 && _vm._hero->vy == 0) {
+ // Set direction of travel if at a node
+ // Note realignment when changing to (thinner) up/down sprite,
+ // otherwise hero could bump into boundaries along route.
+ if (herox < routeNode->x) {
+ setWalk(Common::KEYCODE_RIGHT);
+ } else if (herox > routeNode->x) {
+ setWalk(Common::KEYCODE_LEFT);
+ } else if (heroy < routeNode->y) {
+ setWalk(Common::KEYCODE_DOWN);
+ _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ } else if (heroy > routeNode->y) {
+ setWalk(Common::KEYCODE_UP);
+ _vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1;
+ }
+ }
+}
+
+// Start a new route from hero to cx, cy
+// go_for is the purpose, id indexes the exit or object to walk to
+// Returns FALSE if route not found
+bool Route::startRoute(go_t go_for, int16 id, int16 cx, int16 cy) {
+ debugC(1, kDebugRoute, "startRoute(%d, %d, %d, %d)", go_for, id, cx, cy);
+
+ // Don't attempt to walk if user does not have control
+ if (_vm._hero->pathType != USER)
+ return false;
+
+ status_t &gameStatus = _vm.getGameStatus();
+ // if inventory showing, make it go away
+ if (gameStatus.inventoryState != I_OFF)
+ gameStatus.inventoryState = I_UP;
+
+ gameStatus.go_for = go_for; // Purpose of trip
+ gameStatus.go_id = id; // Index of exit/object
+
+ // Adjust destination to center hero if walking to cursor
+ if (gameStatus.go_for == GO_SPACE)
+ cx -= HERO_MIN_WIDTH / 2;
+
+ bool foundFl = false; // TRUE if route found ok
+ if ((foundFl = findRoute(cx, cy))) { // Found a route?
+ gameStatus.routeIndex = _routeListIndex; // Node index
+ _vm._hero->vx = _vm._hero->vy = 0; // Stop manual motion
+ }
+
+ return foundFl;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/route.h b/engines/hugo/route.h
new file mode 100644
index 0000000000..09b4575fcd
--- /dev/null
+++ b/engines/hugo/route.h
@@ -0,0 +1,85 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_ROUTE_H
+#define HUGO_ROUTE_H
+
+namespace Hugo {
+
+#define kMapBound 1 // Mark a boundary outline
+#define kMapFill 2 // Mark a boundary filled
+#define kMaxSeg 256 // Maximum number of segments
+#define kMaxNodes 256 // Maximum nodes in route
+#define DEBUG_ROUTE FALSE
+
+struct Point {
+ int x;
+ int y;
+};
+
+struct segment_t { // Search segment
+ int16 y; // y position
+ int16 x1, x2; // Range of segment
+};
+
+class Route {
+public:
+ Route(HugoEngine &vm);
+
+ void processRoute();
+ bool startRoute(go_t go_for, short id, short cx, short cy);
+ void setDirection(uint16 keyCode);
+ void setWalk(uint16 direction);
+
+private:
+ HugoEngine &_vm;
+
+ byte _boundaryMap[YPIX][XPIX]; // Boundary byte map
+ segment_t _segment[kMaxSeg]; // List of points in fill-path
+ Point _route[kMaxNodes]; // List of nodes in route (global)
+ int16 _segmentNumb; // Count number of segments
+ int16 _routeListIndex; // Index into route list
+ int16 _destX;
+ int16 _destY;
+ int16 _heroWidth; // Hero width
+ bool _routeFoundFl; // TRUE when path found
+ bool _fullStackFl; // TRUE if stack exhausted
+ bool _fullSegmentFl; // Segments exhausted
+
+ void segment(int16 x, int16 y);
+ bool findRoute(int16 cx, int16 cy);
+ Point *newNode();
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_ROUTE_H
diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp
new file mode 100644
index 0000000000..22eb99c6dd
--- /dev/null
+++ b/engines/hugo/schedule.cpp
@@ -0,0 +1,676 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// This module contains all the scheduling and timing stuff
+
+#include "common/system.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/schedule.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+#define SIGN(X) ((X < 0) ? -1 : 1)
+
+Scheduler::Scheduler(HugoEngine &vm) : _vm(vm) {
+}
+
+Scheduler::~Scheduler() {
+}
+
+// Initialise the timer event queue
+void Scheduler::initEventQueue() {
+ debugC(1, kDebugSchedule, "initEventQueue");
+
+ // Chain next_p from first to last
+ for (int i = kMaxEvents; --i;)
+ _events[i - 1].nextEvent = &_events[i];
+ _events[kMaxEvents - 1].nextEvent = 0;
+
+ // Chain prev_p from last to first
+ for (int i = 1; i < kMaxEvents; i++)
+ _events[i].prevEvent = &_events[i - 1];
+ _events[0].prevEvent = 0;
+
+ _headEvent = _tailEvent = 0; // Event list is empty
+ _freeEvent = _events; // Free list is full
+}
+
+// Return a ptr to an event structure from the free list
+event_t *Scheduler::getQueue() {
+ debugC(4, kDebugSchedule, "getQueue");
+
+ if (!_freeEvent) // Error: no more events available
+ Utils::Error(EVNT_ERR, "%s", "getQueue");
+ event_t *resEvent = _freeEvent;
+ _freeEvent = _freeEvent->nextEvent;
+ resEvent->nextEvent = 0;
+ return resEvent;
+}
+
+// Delete an event structure (i.e. return it to the free list)
+// Historical note: Originally event p was assumed to be at head of queue
+// (i.e. earliest) since all events were deleted in order when proceeding to
+// a new screen. To delete an event from the middle of the queue, the action
+// was overwritten to be ANULL. With the advent of GLOBAL events, Del_queue
+// was modified to allow deletes anywhere in the list, and the DEL_EVENT
+// action was modified to perform the actual delete.
+void Scheduler::delQueue(event_t *curEvent) {
+ debugC(4, kDebugSchedule, "delQueue()");
+
+ if (curEvent == _headEvent) { // If p was the head ptr
+ _headEvent = curEvent->nextEvent; // then make new head_p
+ } else { // Unlink p
+ curEvent->prevEvent->nextEvent = curEvent->nextEvent;
+ if (curEvent->nextEvent)
+ curEvent->nextEvent->prevEvent = curEvent->prevEvent;
+ else
+ _tailEvent = curEvent->prevEvent;
+ }
+
+ if (_headEvent)
+ _headEvent->prevEvent = 0; // Mark end of list
+ else
+ _tailEvent = 0; // Empty queue
+
+ curEvent->nextEvent = _freeEvent; // Return p to free list
+ if (_freeEvent) // Special case, if free list was empty
+ _freeEvent->prevEvent = curEvent;
+ _freeEvent = curEvent;
+}
+
+// Insert the action pointed to by p into the timer event queue
+// The queue goes from head (earliest) to tail (latest) timewise
+void Scheduler::insertAction(act *action) {
+ debugC(1, kDebugSchedule, "insertAction() - Action type A%d", action->a0.actType);
+
+ // First, get and initialise the event structure
+ event_t *curEvent = getQueue();
+ curEvent->action = action;
+ switch (action->a0.actType) { // Assign whether local or global
+ case AGSCHEDULE:
+ curEvent->localActionFl = false; // Lasts over a new screen
+ break;
+ default:
+ curEvent->localActionFl = true; // Rest are for current screen only
+ break;
+ }
+
+ curEvent->time = action->a0.timer + getTicks(); // Convert rel to abs time
+
+ // Now find the place to insert the event
+ if (!_tailEvent) { // Empty queue
+ _tailEvent = _headEvent = curEvent;
+ curEvent->nextEvent = curEvent->prevEvent = 0;
+ } else {
+ event_t *wrkEvent = _tailEvent; // Search from latest time back
+ bool found = false;
+
+ while (wrkEvent && !found) {
+ if (wrkEvent->time <= curEvent->time) { // Found if new event later
+ found = true;
+ if (wrkEvent == _tailEvent) // New latest in list
+ _tailEvent = curEvent;
+ else
+ wrkEvent->nextEvent->prevEvent = curEvent;
+ curEvent->nextEvent = wrkEvent->nextEvent;
+ wrkEvent->nextEvent = curEvent;
+ curEvent->prevEvent = wrkEvent;
+ }
+ wrkEvent = wrkEvent->prevEvent;
+ }
+
+ if (!found) { // Must be earliest in list
+ _headEvent->prevEvent = curEvent; // So insert as new head
+ curEvent->nextEvent = _headEvent;
+ curEvent->prevEvent = 0;
+ _headEvent = curEvent;
+ }
+ }
+}
+
+void Scheduler::insertActionList(uint16 actIndex) {
+// Call Insert_action for each action in the list supplied
+ debugC(1, kDebugSchedule, "insertActionList(%d)", actIndex);
+
+ if (_vm._actListArr[actIndex]) {
+ for (int i = 0; _vm._actListArr[actIndex][i].a0.actType != ANULL; i++)
+ insertAction(&_vm._actListArr[actIndex][i]);
+ }
+}
+
+void Scheduler::decodeString(char *line) {
+// Decode a string
+ debugC(1, kDebugSchedule, "decodeString(%s)", line);
+
+ static const char *cypher = getCypher();
+
+ for (uint16 i = 0; i < strlen(line); i++)
+ line[i] -= cypher[i % strlen(cypher)];
+ debugC(1, kDebugSchedule, "result : %s", line);
+}
+
+event_t *Scheduler::doAction(event_t *curEvent) {
+// This function performs the action in the event structure pointed to by p
+// It dequeues the event and returns it to the free list. It returns a ptr
+// to the next action in the list, except special case of NEW_SCREEN
+ debugC(1, kDebugSchedule, "doAction - Event action type : %d", curEvent->action->a0.actType);
+
+ status_t &gameStatus = _vm.getGameStatus();
+ act *action = curEvent->action;
+ char *response; // User's response string
+ object_t *obj1;
+ object_t *obj2;
+ int dx, dy;
+ event_t *wrkEvent; // Save ev_p->next_p for return
+ event_t *saveEvent; // Used in DEL_EVENTS
+
+ switch (action->a0.actType) {
+ case ANULL: // Big NOP from DEL_EVENTS
+ break;
+ case ASCHEDULE: // act0: Schedule an action list
+ insertActionList(action->a0.actIndex);
+ break;
+ case START_OBJ: // act1: Start an object cycling
+ _vm._objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb;
+ _vm._objects[action->a1.objNumb].cycling = action->a1.cycle;
+ break;
+ case INIT_OBJXY: // act2: Initialise an object
+ _vm._objects[action->a2.objNumb].x = action->a2.x; // Coordinates
+ _vm._objects[action->a2.objNumb].y = action->a2.y;
+ break;
+ case PROMPT: { // act3: Prompt user for key phrase
+// TODO : Add specific code for Hugo 1 DOS, which is handled differently,
+ response = Utils::Box(BOX_PROMPT, "%s", _vm.file().fetchString(action->a3.promptIndex));
+
+ warning("STUB: doAction(act3), expecting answer %s", response);
+
+// TODO : The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block
+#if 0
+ bool found;
+ char *tmpStr; // General purpose string ptr
+
+ for (found = false, dx = 0; !found && (action->a3.responsePtr[dx] != -1); dx++) {
+ tmpStr = _vm.file().Fetch_string(action->a3.responsePtr[dx]);
+ if (strstr(Utils::strlwr(response) , tmpStr))
+ found = true;
+ }
+
+ if (found)
+ insertActionList(action->a3.actPassIndex);
+ else
+ insertActionList(action->a3.actFailIndex);
+#endif
+
+ // HACK: As the answer is not read, currently it's always considered correct
+ insertActionList(action->a3.actPassIndex);
+ break;
+ }
+ case BKGD_COLOR: // act4: Set new background color
+ _vm.screen().setBackgroundColor(action->a4.newBackgroundColor);
+ break;
+ case INIT_OBJVXY: // act5: Initialise an object
+ _vm._objects[action->a5.objNumb].vx = action->a5.vx; // velocities
+ _vm._objects[action->a5.objNumb].vy = action->a5.vy;
+ break;
+ case INIT_CARRY: // act6: Initialise an object
+ _vm._objects[action->a6.objNumb].carriedFl = action->a6.carriedFl; // carried status
+ break;
+ case INIT_HF_COORD: // act7: Initialise an object to hero's "feet" coords
+ _vm._objects[action->a7.objNumb].x = _vm._hero->x - 1;
+ _vm._objects[action->a7.objNumb].y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1;
+ _vm._objects[action->a7.objNumb].screenIndex = *_vm._screen_p; // Don't forget screen!
+ break;
+ case NEW_SCREEN: // act8: Start new screen
+ newScreen(action->a8.screenIndex);
+ break;
+ case INIT_OBJSTATE: // act9: Initialise an object state
+ _vm._objects[action->a9.objNumb].state = action->a9.newState;
+ break;
+ case INIT_PATH: // act10: Initialise an object path and velocity
+ _vm._objects[action->a10.objNumb].pathType = (path_t) action->a10.newPathType;
+ _vm._objects[action->a10.objNumb].vxPath = action->a10.vxPath;
+ _vm._objects[action->a10.objNumb].vyPath = action->a10.vyPath;
+ break;
+ case COND_R: // act11: action lists conditional on object state
+ if (_vm._objects[action->a11.objNumb].state == action->a11.stateReq)
+ insertActionList(action->a11.actPassIndex);
+ else
+ insertActionList(action->a11.actFailIndex);
+ break;
+ case TEXT: // act12: Text box (CF WARN)
+ Utils::Box(BOX_ANY, "%s", _vm.file().fetchString(action->a12.stringIndex)); // Fetch string from file
+ break;
+ case SWAP_IMAGES: // act13: Swap 2 object images
+ swapImages(action->a13.obj1, action->a13.obj2);
+ break;
+ case COND_SCR: // act14: Conditional on current screen
+ if (_vm._objects[action->a14.objNumb].screenIndex == action->a14.screenReq)
+ insertActionList(action->a14.actPassIndex);
+ else
+ insertActionList(action->a14.actFailIndex);
+ break;
+ case AUTOPILOT: // act15: Home in on a (stationary) object
+ // object p1 will home in on object p2
+ obj1 = &_vm._objects[action->a15.obj1];
+ obj2 = &_vm._objects[action->a15.obj2];
+ obj1->pathType = AUTO;
+ dx = obj1->x + obj1->currImagePtr->x1 - obj2->x - obj2->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y1 - obj2->y - obj2->currImagePtr->y1;
+
+ if (dx == 0) // Don't EVER divide by zero!
+ dx = 1;
+ if (dy == 0)
+ dy = 1;
+
+ if (abs(dx) > abs(dy)) {
+ obj1->vx = action->a15.dx * -SIGN(dx);
+ obj1->vy = abs((action->a15.dy * dy) / dx) * -SIGN(dy);
+ } else {
+ obj1->vy = action->a15.dy * -SIGN(dy);
+ obj1->vx = abs((action->a15.dx * dx) / dy) * -SIGN(dx);
+ }
+ break;
+ case INIT_OBJ_SEQ: // act16: Set sequence number to use
+ // Note: Don't set a sequence at time 0 of a new screen, it causes
+ // problems clearing the boundary bits of the object! t>0 is safe
+ _vm._objects[action->a16.objNumb].currImagePtr = _vm._objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr;
+ break;
+ case SET_STATE_BITS: // act17: OR mask with curr obj state
+ _vm._objects[action->a17.objNumb].state |= action->a17.stateMask;
+ break;
+ case CLEAR_STATE_BITS: // act18: AND ~mask with curr obj state
+ _vm._objects[action->a18.objNumb].state &= ~action->a18.stateMask;
+ break;
+ case TEST_STATE_BITS: // act19: If all bits set, do apass else afail
+ if ((_vm._objects[action->a19.objNumb].state & action->a19.stateMask) == action->a19.stateMask)
+ insertActionList(action->a19.actPassIndex);
+ else
+ insertActionList(action->a19.actFailIndex);
+ break;
+ case DEL_EVENTS: // act20: Remove all events of this action type
+ // Note: actions are not deleted here, simply turned into NOPs!
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While events found in list
+ saveEvent = wrkEvent->nextEvent;
+ if (wrkEvent->action->a20.actType == action->a20.actTypeDel)
+ delQueue(wrkEvent);
+ wrkEvent = saveEvent;
+ }
+ break;
+ case GAMEOVER: // act21: Game over!
+ // NOTE: Must wait at least 1 tick before issuing this action if
+ // any objects are to be made invisible!
+ gameStatus.gameOverFl = true;
+ break;
+ case INIT_HH_COORD: // act22: Initialise an object to hero's actual coords
+ _vm._objects[action->a22.objNumb].x = _vm._hero->x;
+ _vm._objects[action->a22.objNumb].y = _vm._hero->y;
+ _vm._objects[action->a22.objNumb].screenIndex = *_vm._screen_p;// Don't forget screen!
+ break;
+ case EXIT: // act23: Exit game back to DOS
+ _vm.endGame();
+ break;
+ case BONUS: // act24: Get bonus score for action
+ processBonus(action->a24.pointIndex);
+ break;
+ case COND_BOX: // act25: Conditional on bounding box
+ obj1 = &_vm._objects[action->a25.objNumb];
+ dx = obj1->x + obj1->currImagePtr->x1;
+ dy = obj1->y + obj1->currImagePtr->y2;
+ if ((dx >= action->a25.x1) && (dx <= action->a25.x2) &&
+ (dy >= action->a25.y1) && (dy <= action->a25.y2))
+ insertActionList(action->a25.actPassIndex);
+ else
+ insertActionList(action->a25.actFailIndex);
+ break;
+ case SOUND: // act26: Play a sound (or tune)
+ if (action->a26.soundIndex < _vm._tunesNbr)
+ _vm.sound().playMusic(action->a26.soundIndex);
+ else
+ _vm.sound().playSound(action->a26.soundIndex, BOTH_CHANNELS, MED_PRI);
+ break;
+ case ADD_SCORE: // act27: Add object's value to score
+ _vm.adjustScore(_vm._objects[action->a27.objNumb].objValue);
+ break;
+ case SUB_SCORE: // act28: Subtract object's value from score
+ _vm.adjustScore(-_vm._objects[action->a28.objNumb].objValue);
+ break;
+ case COND_CARRY: // act29: Conditional on object being carried
+ if (_vm._objects[action->a29.objNumb].carriedFl)
+ insertActionList(action->a29.actPassIndex);
+ else
+ insertActionList(action->a29.actFailIndex);
+ break;
+ case INIT_MAZE: // act30: Enable and init maze structure
+ _maze.enabledFl = true;
+ _maze.size = action->a30.mazeSize;
+ _maze.x1 = action->a30.x1;
+ _maze.y1 = action->a30.y1;
+ _maze.x2 = action->a30.x2;
+ _maze.y2 = action->a30.y2;
+ _maze.x3 = action->a30.x3;
+ _maze.x4 = action->a30.x4;
+ _maze.firstScreenIndex = action->a30.firstScreenIndex;
+ break;
+ case EXIT_MAZE: // act31: Disable maze mode
+ _maze.enabledFl = false;
+ break;
+ case INIT_PRIORITY:
+ _vm._objects[action->a32.objNumb].priority = action->a32.priority;
+ break;
+ case INIT_SCREEN:
+ _vm._objects[action->a33.objNumb].screenIndex = action->a33.screenIndex;
+ break;
+ case AGSCHEDULE: // act34: Schedule a (global) action list
+ insertActionList(action->a34.actIndex);
+ break;
+ case REMAPPAL: // act35: Remap a palette color
+ _vm.screen().remapPal(action->a35.oldColorIndex, action->a35.newColorIndex);
+ break;
+ case COND_NOUN: // act36: Conditional on noun mentioned
+ if (_vm.parser().isWordPresent(_vm._arrayNouns[action->a36.nounIndex]))
+ insertActionList(action->a36.actPassIndex);
+ else
+ insertActionList(action->a36.actFailIndex);
+ break;
+ case SCREEN_STATE: // act37: Set new screen state
+ _vm._screenStates[action->a37.screenIndex] = action->a37.newState;
+ break;
+ case INIT_LIPS: // act38: Position lips on object
+ _vm._objects[action->a38.lipsObjNumb].x = _vm._objects[action->a38.objNumb].x + action->a38.dxLips;
+ _vm._objects[action->a38.lipsObjNumb].y = _vm._objects[action->a38.objNumb].y + action->a38.dyLips;
+ _vm._objects[action->a38.lipsObjNumb].screenIndex = *_vm._screen_p; // Don't forget screen!
+ _vm._objects[action->a38.lipsObjNumb].cycling = CYCLE_FORWARD;
+ break;
+ case INIT_STORY_MODE: // act39: Init story_mode flag
+ // This is similar to the QUIET path mode, except that it is
+ // independant of it and it additionally disables the ">" prompt
+ gameStatus.storyModeFl = action->a39.storyModeFl;
+
+ // End the game after story if this is special vendor demo mode
+ if (gameStatus.demoFl && action->a39.storyModeFl == false)
+ _vm.endGame();
+ break;
+ case WARN: // act40: Text box (CF TEXT)
+ Utils::Box(BOX_OK, "%s", _vm.file().fetchString(action->a40.stringIndex));
+ break;
+ case COND_BONUS: // act41: Perform action if got bonus
+ if (_vm._points[action->a41.BonusIndex].scoredFl)
+ insertActionList(action->a41.actPassIndex);
+ else
+ insertActionList(action->a41.actFailIndex);
+ break;
+ case TEXT_TAKE: // act42: Text box with "take" message
+ Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[_vm._objects[action->a42.objNumb].nounIndex][TAKE_NAME]);
+ break;
+ case YESNO: // act43: Prompt user for Yes or No
+ warning("doAction(act43) - Yes/No Box");
+ if (Utils::Box(BOX_YESNO, "%s", _vm.file().fetchString(action->a43.promptIndex)) != 0)
+ insertActionList(action->a43.actYesIndex);
+ else
+ insertActionList(action->a43.actNoIndex);
+ break;
+ case STOP_ROUTE: // act44: Stop any route in progress
+ gameStatus.routeIndex = -1;
+ break;
+ case COND_ROUTE: // act45: Conditional on route in progress
+ if (gameStatus.routeIndex >= action->a45.routeIndex)
+ insertActionList(action->a45.actPassIndex);
+ else
+ insertActionList(action->a45.actFailIndex);
+ break;
+ case INIT_JUMPEXIT: // act46: Init status.jumpexit flag
+ // This is to allow left click on exit to get there immediately
+ // For example the plane crash in Hugo2 where hero is invisible
+ // Couldn't use INVISIBLE flag since conflicts with boat in Hugo1
+ gameStatus.jumpExitFl = action->a46.jumpExitFl;
+ break;
+ case INIT_VIEW: // act47: Init object.viewx, viewy, dir
+ _vm._objects[action->a47.objNumb].viewx = action->a47.viewx;
+ _vm._objects[action->a47.objNumb].viewy = action->a47.viewy;
+ _vm._objects[action->a47.objNumb].direction = action->a47.direction;
+ break;
+ case INIT_OBJ_FRAME: // act48: Set seq,frame number to use
+ // Note: Don't set a sequence at time 0 of a new screen, it causes
+ // problems clearing the boundary bits of the object! t>0 is safe
+ _vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].seqList[action->a48.seqIndex].seqPtr;
+ for (dx = 0; dx < action->a48.frameIndex; dx++)
+ _vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].currImagePtr->nextSeqPtr;
+ break;
+ case OLD_SONG:
+ //TODO For Hugo 1 and Hugo2 DOS: The songs were not stored in a DAT file, but directly as
+ //strings. the current play_music should be modified to use a strings instead of reading
+ //the file, in those cases. This replaces, for those DOS versions, act26.
+ warning("STUB: doAction(act49)");
+ break;
+ default:
+ Utils::Error(EVNT_ERR, "%s", "doAction");
+ break;
+ }
+
+ if (action->a0.actType == NEW_SCREEN) { // New_screen() deletes entire list
+ return 0; // next_p = 0 since list now empty
+ } else {
+ wrkEvent = curEvent->nextEvent;
+ delQueue(curEvent); // Return event to free list
+ return wrkEvent; // Return next event ptr
+ }
+}
+
+// This is the scheduler which runs every tick. It examines the event queue
+// for any events whose time has come. It dequeues these events and performs
+// the action associated with the event, returning it to the free queue
+void Scheduler::runScheduler() {
+ debugC(6, kDebugSchedule, "runScheduler");
+
+ status_t &gameStatus = _vm.getGameStatus();
+ event_t *curEvent = _headEvent; // The earliest event
+
+ while (curEvent && curEvent->time <= gameStatus.tick) // While mature events found
+ curEvent = doAction(curEvent); // Perform the action (returns next_p)
+ gameStatus.tick++; // Accessed elsewhere via getTicks()
+}
+
+uint32 Scheduler::getTicks() {
+// Return system time in ticks. A tick is 1/TICKS_PER_SEC mS
+ debugC(3, kDebugSchedule, "getTicks");
+
+ return _vm.getGameStatus().tick;
+}
+
+void Scheduler::processBonus(int bonusIndex) {
+// Add indecated bonus to score if not added already
+ debugC(1, kDebugSchedule, "processBonus(%d)", bonusIndex);
+
+ if (!_vm._points[bonusIndex].scoredFl) {
+ _vm.adjustScore(_vm._points[bonusIndex].score);
+ _vm._points[bonusIndex].scoredFl = true;
+ }
+}
+
+// Transition to a new screen as follows:
+// 1. Clear out all non-global events from event list.
+// 2. Set the new screen (in the hero object and any carried objects)
+// 3. Read in the screen files for the new screen
+// 4. Schedule action list for new screen
+// 5. Initialise prompt line and status line
+void Scheduler::newScreen(int screenIndex) {
+ debugC(1, kDebugSchedule, "newScreen(%d)", screenIndex);
+
+ // Make sure the background file exists!
+ if (!_vm.isPacked()) {
+ char line[32];
+ if (!_vm.file().fileExists(strcat(strncat(strcpy(line, _vm._picDir), _vm._screenNames[screenIndex], NAME_LEN), BKGEXT)) &&
+ !_vm.file().fileExists(strcat(strcpy(line, _vm._screenNames[screenIndex]), ".ART"))) {
+ Utils::Box(BOX_ANY, "%s", _vm._textSchedule[kSsNoBackground]);
+ return;
+ }
+ }
+
+ // 1. Clear out all local events
+ event_t *curEvent = _headEvent; // The earliest event
+ event_t *wrkEvent; // Event ptr
+ while (curEvent) { // While mature events found
+ wrkEvent = curEvent->nextEvent; // Save p (becomes undefined after Del)
+ if (curEvent->localActionFl)
+ delQueue(curEvent); // Return event to free list
+ curEvent = wrkEvent;
+ }
+
+ // 2. Set the new screen in the hero object and any being carried
+ _vm.setNewScreen(screenIndex);
+
+ // 3. Read in new screen files
+ _vm.readScreenFiles(screenIndex);
+
+ // 4. Schedule action list for this screen
+ _vm.screenActions(screenIndex);
+
+ // 5. Initialise prompt line and status line
+ _vm.initNewScreenDisplay();
+}
+
+// Write the event queue to the file with handle f
+// Note that we convert all the event structure ptrs to indexes
+// using -1 for NULL. We can't convert the action ptrs to indexes
+// so we save address of first dummy action ptr to compare on restore.
+void Scheduler::saveEvents(Common::WriteStream *f) {
+ debugC(1, kDebugSchedule, "saveEvents()");
+
+ uint32 curTime = getTicks();
+ event_t saveEventArr[kMaxEvents]; // Convert event ptrs to indexes
+
+ // Convert event ptrs to indexes
+ for (int16 i = 0; i < kMaxEvents; i++) {
+ event_t *wrkEvent = &_events[i];
+ saveEventArr[i] = *wrkEvent;
+ saveEventArr[i].prevEvent = (wrkEvent->prevEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events);
+ saveEventArr[i].nextEvent = (wrkEvent->nextEvent == 0) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events);
+ }
+
+ int16 freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events;
+ int16 headIndex = (_headEvent == 0) ? -1 : _headEvent - _events;
+ int16 tailIndex = (_tailEvent == 0) ? -1 : _tailEvent - _events;
+
+ f->write(&curTime, sizeof(curTime));
+ f->write(&freeIndex, sizeof(freeIndex));
+ f->write(&headIndex, sizeof(headIndex));
+ f->write(&tailIndex, sizeof(tailIndex));
+ f->write(saveEventArr, sizeof(saveEventArr));
+}
+
+// Restore the event list from file with handle f
+void Scheduler::restoreEvents(Common::SeekableReadStream *f) {
+ debugC(1, kDebugSchedule, "restoreEvents");
+
+ uint32 saveTime;
+ int16 freeIndex; // Free list index
+ int16 headIndex; // Head of list index
+ int16 tailIndex; // Tail of list index
+ event_t savedEvents[kMaxEvents]; // Convert event ptrs to indexes
+
+ f->read(&saveTime, sizeof(saveTime)); // time of save
+ f->read(&freeIndex, sizeof(freeIndex));
+ f->read(&headIndex, sizeof(headIndex));
+ f->read(&tailIndex, sizeof(tailIndex));
+ f->read(savedEvents, sizeof(savedEvents));
+
+ event_t *wrkEvent;
+ // Restore events indexes to pointers
+ for (int i = 0; i < kMaxEvents; i++) {
+ wrkEvent = &savedEvents[i];
+ _events[i] = *wrkEvent;
+ _events[i].prevEvent = (wrkEvent->prevEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->prevEvent ];
+ _events[i].nextEvent = (wrkEvent->nextEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->nextEvent ];
+ }
+ _freeEvent = (freeIndex == -1) ? 0 : &_events[freeIndex];
+ _headEvent = (headIndex == -1) ? 0 : &_events[headIndex];
+ _tailEvent = (tailIndex == -1) ? 0 : &_events[tailIndex];
+
+ // Adjust times to fit our time
+ uint32 curTime = getTicks();
+ wrkEvent = _headEvent; // The earliest event
+ while (wrkEvent) { // While mature events found
+ wrkEvent->time = wrkEvent->time - saveTime + curTime;
+ wrkEvent = wrkEvent->nextEvent;
+ }
+}
+
+void Scheduler::restoreScreen(int screenIndex) {
+// Transition to a new screen as follows:
+// 1. Set the new screen (in the hero object and any carried objects)
+// 2. Read in the screen files for the new screen
+// 3. Initialise prompt line and status line
+
+ debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex);
+
+ // 1. Set the new screen in the hero object and any being carried
+ _vm.setNewScreen(screenIndex);
+
+ // 2. Read in new screen files
+ _vm.readScreenFiles(screenIndex);
+
+ // 3. Initialise prompt line and status line
+ _vm.initNewScreenDisplay();
+}
+
+void Scheduler::swapImages(int objNumb1, int objNumb2) {
+// Swap all the images of one object with another. Set hero_image (we make
+// the assumption for now that the first obj is always the HERO) to the object
+// number of the swapped image
+ debugC(1, kDebugSchedule, "swapImages(%d, %d)", objNumb1, objNumb2);
+
+ _vm.file().saveSeq(&_vm._objects[objNumb1]);
+
+ seqList_t tmpSeqList[MAX_SEQUENCES];
+ int seqListSize = sizeof(seqList_t) * MAX_SEQUENCES;
+
+ memcpy(tmpSeqList, _vm._objects[objNumb1].seqList, seqListSize);
+ memcpy(_vm._objects[objNumb1].seqList, _vm._objects[objNumb2].seqList, seqListSize);
+ memcpy(_vm._objects[objNumb2].seqList, tmpSeqList, seqListSize);
+ _vm.file().restoreSeq(&_vm._objects[objNumb1]);
+ _vm._objects[objNumb2].currImagePtr = _vm._objects[objNumb2].seqList[0].seqPtr;
+ _vm._heroImage = (_vm._heroImage == HERO) ? objNumb2 : HERO;
+
+ // Make sure baseline stays constant
+ _vm._objects[objNumb1].y += _vm._objects[objNumb2].currImagePtr->y2 - _vm._objects[objNumb1].currImagePtr->y2;
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h
new file mode 100644
index 0000000000..b77abd4302
--- /dev/null
+++ b/engines/hugo/schedule.h
@@ -0,0 +1,105 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_SCHEDULE_H
+#define HUGO_SCHEDULE_H
+
+namespace Hugo {
+
+#define kMaxEvents 50 // Max events in event queue
+
+struct event_t {
+ act *action; // Ptr to action to perform
+ bool localActionFl; // true if action is only for this screen
+ uint32 time; // (absolute) time to perform action
+ struct event_t *prevEvent; // Chain to previous event
+ struct event_t *nextEvent; // Chain to next event
+};
+
+class Scheduler {
+public:
+ Scheduler(HugoEngine &vm);
+ virtual ~Scheduler();
+
+ void initEventQueue();
+ void insertAction(act *action);
+ void insertActionList(uint16 actIndex);
+ void decodeString(char *line);
+ void runScheduler();
+ uint32 getTicks();
+ void processBonus(int bonusIndex);
+ void newScreen(int screenIndex);
+ void restoreEvents(Common::SeekableReadStream *f);
+ void saveEvents(Common::WriteStream *f);
+ void restoreScreen(int screenIndex);
+ void swapImages(int objNumb1, int objNumb2);
+
+private:
+ enum seqTextSchedule {
+ kSsNoBackground = 0,
+ kSsBadSaveGame = 1
+ };
+
+ HugoEngine &_vm;
+
+ event_t _events[kMaxEvents]; // Statically declare event structures
+
+ event_t *_freeEvent; // Free list of event structures
+ event_t *_headEvent; // Head of list (earliest time)
+ event_t *_tailEvent; // Tail of list (latest time)
+
+ event_t *getQueue();
+ void delQueue(event_t *curEvent);
+ event_t *doAction(event_t *curEvent);
+
+ virtual const char *getCypher() = 0;
+};
+
+class Scheduler_v1d : public Scheduler {
+public:
+ Scheduler_v1d(HugoEngine &vm);
+ ~Scheduler_v1d();
+
+ const char *getCypher();
+};
+
+class Scheduler_v3d : public Scheduler {
+public:
+ Scheduler_v3d(HugoEngine &vm);
+ ~Scheduler_v3d();
+
+ const char *getCypher();
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_SCHEDULE_H
diff --git a/engines/hugo/schedule_v1d.cpp b/engines/hugo/schedule_v1d.cpp
new file mode 100644
index 0000000000..81daf639f6
--- /dev/null
+++ b/engines/hugo/schedule_v1d.cpp
@@ -0,0 +1,52 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// This module contains all the scheduling and timing stuff
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+Scheduler_v1d::Scheduler_v1d(HugoEngine &vm) : Scheduler(vm) {
+}
+
+Scheduler_v1d::~Scheduler_v1d() {
+}
+
+const char *Scheduler_v1d::getCypher() {
+ return "Copyright 1991, Gray Design Associates";
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/schedule_v3d.cpp b/engines/hugo/schedule_v3d.cpp
new file mode 100644
index 0000000000..9421b0f5f9
--- /dev/null
+++ b/engines/hugo/schedule_v3d.cpp
@@ -0,0 +1,51 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// This module contains all the scheduling and timing stuff
+
+#include "common/system.h"
+
+#include "hugo/hugo.h"
+#include "hugo/schedule.h"
+
+namespace Hugo {
+
+Scheduler_v3d::Scheduler_v3d(HugoEngine &vm) : Scheduler(vm) {
+}
+
+Scheduler_v3d::~Scheduler_v3d() {
+}
+
+const char *Scheduler_v3d::getCypher() {
+ return "Copyright 1992, Gray Design Associates";
+}
+} // End of namespace Hugo
diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp
new file mode 100644
index 0000000000..e5f55afcc8
--- /dev/null
+++ b/engines/hugo/sound.cpp
@@ -0,0 +1,331 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+/* sound.c - sound effects and music support */
+
+#include "common/system.h"
+
+#include "sound/decoders/raw.h"
+#include "sound/audiostream.h"
+#include "sound/midiparser.h"
+#include "sound/mididrv.h"
+
+#include "hugo/hugo.h"
+#include "hugo/game.h"
+#include "hugo/file.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+class MidiPlayer : public MidiDriver {
+public:
+
+ enum {
+ NUM_CHANNELS = 16
+ };
+
+ MidiPlayer(MidiDriver *driver);
+ ~MidiPlayer();
+
+ void play(uint8 *stream, uint16 size);
+ void stop();
+ void pause(bool p);
+ void updateTimer();
+ void adjustVolume(int diff);
+ void setVolume(int volume);
+ int getVolume() const { return _masterVolume; }
+ void setLooping(bool loop) { _isLooping = loop; }
+
+ // MidiDriver interface
+ 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; }
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+private:
+
+ static void timerCallback(void *p);
+
+ MidiDriver *_driver;
+ MidiParser *_parser;
+ uint8 *_midiData;
+ bool _isLooping;
+ bool _isPlaying;
+ bool _paused;
+ int _masterVolume;
+ MidiChannel *_channelsTable[NUM_CHANNELS];
+ uint8 _channelsVolume[NUM_CHANNELS];
+ Common::Mutex _mutex;
+};
+
+MidiPlayer::MidiPlayer(MidiDriver *driver)
+ : _driver(driver), _parser(0), _midiData(0), _isLooping(false), _isPlaying(false), _paused(false), _masterVolume(0) {
+ assert(_driver);
+ memset(_channelsTable, 0, sizeof(_channelsTable));
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ _channelsVolume[i] = 127;
+ }
+}
+
+MidiPlayer::~MidiPlayer() {
+ close();
+}
+
+void MidiPlayer::play(uint8 *stream, uint16 size) {
+ if (!stream) {
+ stop();
+ return;
+ }
+
+ _midiData = (uint8 *)malloc(size);
+ if (_midiData) {
+ memcpy(_midiData, stream, size);
+ _mutex.lock();
+ _parser->loadMusic(_midiData, size);
+ _parser->setTrack(0);
+ _isLooping = true;
+ _isPlaying = true;
+ _mutex.unlock();
+ }
+}
+
+void MidiPlayer::stop() {
+ _mutex.lock();
+ if (_isPlaying) {
+ _isPlaying = false;
+ _parser->unloadMusic();
+ free(_midiData);
+ _midiData = 0;
+ }
+ _mutex.unlock();
+}
+
+void MidiPlayer::pause(bool p) {
+ _paused = p;
+
+ for (int i = 0; i < NUM_CHANNELS; ++i) {
+ if (_channelsTable[i]) {
+ _channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
+ }
+ }
+}
+
+void MidiPlayer::updateTimer() {
+ if (_paused) {
+ return;
+ }
+
+ _mutex.lock();
+ if (_isPlaying) {
+ _parser->onTimer();
+ }
+ _mutex.unlock();
+}
+
+void MidiPlayer::adjustVolume(int diff) {
+ setVolume(_masterVolume + diff);
+}
+
+void MidiPlayer::setVolume(int volume) {
+ _masterVolume = CLIP(volume, 0, 255);
+ _mutex.lock();
+ for (int i = 0; i < NUM_CHANNELS; ++i) {
+ if (_channelsTable[i]) {
+ _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
+ }
+ }
+ _mutex.unlock();
+}
+
+int MidiPlayer::open() {
+ _driver->open();
+
+ _parser = MidiParser::createParser_SMF();
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(_driver->getBaseTempo());
+ _driver->setTimerCallback(this, &timerCallback);
+
+ return 0;
+}
+
+void MidiPlayer::close() {
+ stop();
+ _mutex.lock();
+ _driver->setTimerCallback(0, 0);
+ _driver->close();
+ delete _driver;
+ _driver = 0;
+ _parser->setMidiDriver(0);
+ delete _parser;
+ _mutex.unlock();
+}
+
+void MidiPlayer::send(uint32 b) {
+ byte volume, ch = (byte)(b & 0xF);
+ switch (b & 0xFFF0) {
+ case 0x07B0: // volume change
+ volume = (byte)((b >> 16) & 0x7F);
+ _channelsVolume[ch] = volume;
+ volume = volume * _masterVolume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ break;
+ case 0x7BB0: // all notes off
+ if (!_channelsTable[ch]) {
+ // channel not yet allocated, no need to send the event
+ return;
+ }
+ break;
+ }
+
+ if (!_channelsTable[ch]) {
+ _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ }
+ if (_channelsTable[ch]) {
+ _channelsTable[ch]->send(b);
+ }
+}
+
+void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
+ switch (type) {
+ case 0x2F: // end of Track
+ if (_isLooping) {
+ _parser->jumpToTick(0);
+ } else {
+ stop();
+ }
+ break;
+ default:
+// warning("Unhandled meta event: %02x", type);
+ break;
+ }
+}
+
+void MidiPlayer::timerCallback(void *p) {
+ MidiPlayer *player = (MidiPlayer *)p;
+
+ player->updateTimer();
+}
+
+SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
+
+ _midiPlayer = new MidiPlayer(driver);
+}
+
+void SoundHandler::setMusicVolume() {
+ /* Set the FM music volume from config.mvolume (0..100%) */
+
+ _midiPlayer->setVolume(_config.musicVolume * 255 / 100);
+}
+
+void SoundHandler::stopSound() {
+ /* Stop any sound that might be playing */
+ _vm._mixer->stopAll();
+}
+
+void SoundHandler::stopMusic() {
+ /* Stop any tune that might be playing */
+ _midiPlayer->stop();
+}
+
+void SoundHandler::toggleMusic() {
+// Turn music on and off
+ _config.musicFl = !_config.musicFl;
+
+ _midiPlayer->pause(_config.musicFl);
+}
+
+void SoundHandler::toggleSound() {
+// Turn digitized sound on and off
+ _config.soundFl = !_config.soundFl;
+}
+
+void SoundHandler::playMIDI(sound_pt seq_p, uint16 size) {
+ _midiPlayer->play(seq_p, size);
+}
+
+
+void SoundHandler::playMusic(int16 tune) {
+ /* Read a tune sequence from the sound database and start playing it */
+ sound_pt seqPtr; // Sequence data from file
+ uint16 size; // Size of sequence data
+
+ if (_config.musicFl) {
+ _vm.getGameStatus().song = tune;
+ seqPtr = _vm.file().getSound(tune, &size);
+ playMIDI(seqPtr, size);
+ }
+}
+
+
+void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
+ /* Produce various sound effects on supplied stereo channel(s) */
+ /* Override currently playing sound only if lower or same priority */
+
+ // uint32 dwVolume; // Left, right volume of sound
+ sound_pt sound_p; // Sound data
+ uint16 size; // Size of data
+ static byte curPriority = 0; // Priority of currently playing sound
+ //
+ /* Sound disabled */
+ if (!_config.soundFl || !_vm._mixer->isReady())
+ return;
+ //
+ // // See if last wave still playing - if so, check priority
+ // if (waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)) == WAVERR_STILLPLAYING)
+ // if (priority < curPriority) // Don't override unless priority >= current
+ // return;
+ // else
+ // Stop_sound();
+ curPriority = priority;
+ //
+ /* Get sound data */
+ if ((sound_p = _vm.file().getSound(sound, &size)) == 0)
+ return;
+
+ Audio::AudioStream *stream = Audio::makeRawStream(sound_p, size, 11025, Audio::FLAG_UNSIGNED);
+ _vm._mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
+
+}
+
+void SoundHandler::initSound() {
+ /* Initialize for MCI sound and midi */
+
+ _midiPlayer->open();
+}
+
+} // End of namespace Hugo
diff --git a/engines/hugo/sound.h b/engines/hugo/sound.h
new file mode 100644
index 0000000000..53a5912a92
--- /dev/null
+++ b/engines/hugo/sound.h
@@ -0,0 +1,65 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_SOUND_H
+#define HUGO_SOUND_H
+
+#include "sound/mixer.h"
+
+namespace Hugo {
+
+class MidiPlayer;
+
+class SoundHandler {
+public:
+ SoundHandler(HugoEngine &vm);
+
+ void toggleMusic();
+ void toggleSound();
+ void setMusicVolume();
+ void playMusic(short tune);
+ void playSound(short sound, stereo_t channel, byte priority);
+ void initSound();
+
+private:
+ HugoEngine &_vm;
+ Audio::SoundHandle _soundHandle;
+ MidiPlayer *_midiPlayer;
+
+ void stopSound();
+ void stopMusic();
+ void playMIDI(sound_pt seq_p, uint16 size);
+};
+
+} // End of namespace Hugo
+
+#endif //HUGO_SOUND_H
diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp
new file mode 100644
index 0000000000..8cca4b7d84
--- /dev/null
+++ b/engines/hugo/util.cpp
@@ -0,0 +1,205 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "gui/message.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+int Utils::firstBit(byte data) {
+// Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found
+ if (!data)
+ return 8;
+
+ int i;
+ for (i = 0; i < 8; i++) {
+ if ((data << i) & 0x80)
+ break;
+ }
+
+ return i;
+}
+
+int Utils::lastBit(byte data) {
+// Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found
+ if (!data)
+ return 8;
+
+ int i;
+ for (i = 7; i >= 0; i--) {
+ if ((data << i) & 0x80)
+ break;
+ }
+
+ return i;
+}
+
+void Utils::reverseByte(byte *data) {
+// Reverse the bit order in supplied byte
+ byte maskIn = 0x80;
+ byte maskOut = 0x01;
+ byte result = 0;
+
+ for (byte i = 0; i < 8; i++, maskIn >>= 1, maskOut <<= 1) {
+ if (*data & maskIn)
+ result |= maskOut;
+ }
+
+ *data = result;
+}
+
+char *Utils::Box(box_t dismiss, const char *s, ...) {
+ static char buffer[MAX_STRLEN + 1]; // Format text into this
+
+ if (!s)
+ return 0; // NULL strings catered for
+
+ if (s[0] == '\0')
+ return 0;
+
+ if (strlen(s) > MAX_STRLEN - 100) { // Test length
+ Warn("String too big:\n%s", s);
+ return 0;
+ }
+
+ va_list marker;
+ va_start(marker, s);
+ vsprintf(buffer, s, marker); // Format string into buffer
+ va_end(marker);
+
+ if (buffer[0] == '\0')
+ return 0;
+
+ switch(dismiss) {
+ case BOX_ANY:
+ case BOX_OK: {
+ GUI::MessageDialog dialog(buffer, "OK");
+ dialog.runModal();
+ break;
+ }
+ case BOX_YESNO: {
+ GUI::MessageDialog dialog(buffer, "YES", "NO");
+ if (dialog.runModal() == GUI::kMessageOK)
+ return buffer;
+ return 0;
+ break;
+ }
+ case BOX_PROMPT:
+ warning("Box: unhandled BOX_PROMPT");
+ int boxTime = strlen(buffer) * 30;
+ GUI::TimedMessageDialog dialog(buffer, MAX(1500, boxTime));
+ dialog.runModal();
+ // TODO: Some boxes (i.e. the combination code for the shed), needs to return an input.
+ }
+
+ return buffer;
+}
+
+void Utils::Warn(const char *format, ...) {
+// Warning handler. Print supplied message and continue
+// Arguments are same as printf
+ char buffer[WARNLEN];
+ va_list marker;
+ va_start(marker, format);
+ vsnprintf(buffer, WARNLEN, format, marker);
+ va_end(marker);
+ warning("Hugo warning: %s", buffer);
+}
+
+void Utils::Error(int error_type, const char *format, ...) {
+// Fatal error handler. Reset environment, print error and exit
+// Arguments are same as printf
+ char buffer[ERRLEN + 1];
+ bool fatal = true; // Fatal error, else continue
+
+ switch (error_type) {
+ case FILE_ERR:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr1]);
+ break;
+ case WRITE_ERR:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr2]);
+ fatal = false; // Allow continuation
+ break;
+ case PCCH_ERR:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr3]);
+ break;
+ case HEAP_ERR:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr4]);
+ break;
+ case SOUND_ERR:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr5]);
+ break;
+ default:
+ strcpy(buffer, HugoEngine::get()._textUtil[kErr6]);
+ break;
+ }
+
+ if (fatal)
+ HugoEngine::get().shutdown(); // Restore any devices before exit
+
+ va_list marker;
+ va_start(marker, format);
+ vsnprintf(&buffer[strlen(buffer)], ERRLEN - strlen(buffer), format, marker);
+ va_end(marker);
+ //MessageBeep(MB_ICONEXCLAMATION);
+ //MessageBox(hwnd, buffer, "HugoWin Error", MB_OK | MB_ICONEXCLAMATION);
+ warning("Hugo Error: %s", buffer);
+
+ if (fatal)
+ exit(1);
+}
+
+void Utils::gameOverMsg(void) {
+ // Print options for user when dead
+ //MessageBox(hwnd, gameoverstring, "Be more careful next time!", MB_OK | MB_ICONINFORMATION);
+ warning("STUB: Gameover_msg(): %s", HugoEngine::get()._textUtil[kGameOver]);
+}
+
+char *Utils::strlwr(char *buffer) {
+ char *result = buffer;
+
+ while (*buffer != '\0') {
+ if (isupper(*buffer))
+ *buffer = tolower(*buffer);
+ buffer++;
+ }
+
+ return result;
+}
+
+
+} // End of namespace Hugo
diff --git a/engines/hugo/util.h b/engines/hugo/util.h
new file mode 100644
index 0000000000..69428fb3bb
--- /dev/null
+++ b/engines/hugo/util.h
@@ -0,0 +1,66 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_UTIL_H
+#define HUGO_UTIL_H
+
+namespace Hugo {
+
+enum seqTextUtil {
+ kTech = 0,
+ kErr1 = 1,
+ kErr2 = 2,
+ kErr3 = 3,
+ kErr4 = 4,
+ kErr5 = 5,
+ kErr6 = 6,
+ kGameOver = 7
+// kObsoleteErr1 = 8,
+// kObsoleteErr2 = 9
+};
+
+namespace Utils {
+int firstBit(byte data);
+int lastBit(byte data);
+
+void gameOverMsg();
+void reverseByte(byte *data);
+void Error(int code, const char *format, ...) GCC_PRINTF(2, 3);
+void Warn(const char *format, ...) GCC_PRINTF(1, 2);
+
+char *Box(box_t, const char *, ...) GCC_PRINTF(2, 3);
+char *strlwr(char *buffer);
+}
+
+} // End of namespace Hugo
+
+#endif
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
index 225b44b3f4..fc509700d7 100644
--- a/engines/kyra/debugger.cpp
+++ b/engines/kyra/debugger.cpp
@@ -76,7 +76,7 @@ bool Debugger::cmd_loadPalette(int argc, const char **argv) {
return true;
}
- if (_vm->gameFlags().gameID != GI_KYRA1 && _vm->resource()->getFileSize(argv[1]) != 768) {
+ if (_vm->game() != GI_KYRA1 && _vm->resource()->getFileSize(argv[1]) != 768) {
uint8 buffer[320*200];
_vm->screen()->copyRegionToBuffer(5, 0, 0, 320, 200, buffer);
_vm->screen()->loadBitmap(argv[1], 5, 5, 0);
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 135a9ae7b2..875b49b62d 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -43,6 +43,13 @@ struct KYRAGameDescription {
#include "kyra/detection_tables.h"
+namespace {
+
+const char * const directoryGlobs[] = {
+ "malcolm",
+ 0
+};
+
const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)adGameDescs,
@@ -63,11 +70,13 @@ const ADParams detectionParams = {
// Additional GUI options (for every game}
Common::GUIO_NONE,
// Maximum directory depth
- 1,
+ 2,
// List of directory globs
- 0
+ directoryGlobs
};
+} // End of anonymous namespace
+
class KyraMetaEngine : public AdvancedMetaEngine {
public:
KyraMetaEngine() : AdvancedMetaEngine(detectionParams) {}
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 2e1d5afc17..84e0f343a7 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1080,6 +1080,22 @@ const KYRAGameDescription adGameDescs[] = {
"lol",
0,
{
+ { "WESTWOOD.1", 0, "320b2828be595c491903f467094f05eb", -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 }
},
diff --git a/engines/kyra/gui.cpp b/engines/kyra/gui.cpp
index 9f5d29d7b9..f0eb25190b 100644
--- a/engines/kyra/gui.cpp
+++ b/engines/kyra/gui.cpp
@@ -92,7 +92,7 @@ void GUI::initMenu(Menu &menu) {
textY = menu.y + menu.titleY;
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
printMenuText(getMenuTitle(menu), textX, textY, menu.textColor, 0, 9);
} else {
if (_vm->gameFlags().platform != Common::kPlatformAmiga)
@@ -136,7 +136,7 @@ void GUI::initMenu(Menu &menu) {
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
textY = y1 + 2;
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
textY++;
if (i == menu.highlightedItem)
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
@@ -162,7 +162,7 @@ void GUI::initMenu(Menu &menu) {
for (int i = 0; i < menu.numberOfItems; ++i) {
if (getMenuItemLabel(menu.item[i])) {
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
menu.item[i].labelX = menu.item[i].x - 1;
menu.item[i].labelY = menu.item[i].y + 3;
printMenuText(getMenuItemLabel(menu.item[i]), menu.x + menu.item[i].labelX, menu.y + menu.item[i].labelY, menu.item[i].textColor, 0, 10);
@@ -206,7 +206,7 @@ void GUI::processHighlights(Menu &menu) {
int mouseX = p.x;
int mouseY = p.y;
- if (_vm->_flags.gameID == GI_LOL && menu.highlightedItem != 255) {
+ if (_vm->game() == GI_LOL && menu.highlightedItem != 255) {
// LoL doesnt't have default highlighted items.
// We use a highlightedItem value of 255 for this.
@@ -230,8 +230,8 @@ void GUI::processHighlights(Menu &menu) {
if (mouseX > x1 && mouseX < x2 &&
mouseY > y1 && mouseY < y2) {
- if (menu.highlightedItem != i || _vm->_flags.gameID == GI_LOL) {
- if (_vm->_flags.gameID != GI_LOL) {
+ if (menu.highlightedItem != i || _vm->game() == GI_LOL) {
+ if (_vm->game() != GI_LOL) {
if (menu.item[menu.highlightedItem].enabled)
redrawText(menu);
}
@@ -260,7 +260,7 @@ void GUI::redrawText(const Menu &menu) {
textX = getMenuCenterStringX(getMenuItemTitle(menu.item[i]), x1, x2);
int textY = y1 + 2;
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
textY++;
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].textColor, 0, 8);
} else {
@@ -290,7 +290,7 @@ void GUI::redrawHighlight(const Menu &menu) {
int textY = y1 + 2;
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
textY++;
printMenuText(getMenuItemTitle(menu.item[i]), textX, textY, menu.item[i].highlightColor, 0, 8);
} else {
@@ -399,7 +399,7 @@ void GUI::updateSaveList(bool excludeQuickSaves) {
int GUI::getNextSavegameSlot() {
Common::InSaveFile *in;
- int start = _vm->gameFlags().gameID == GI_LOL ? 0 : 1;
+ int start = _vm->game() == GI_LOL ? 0 : 1;
for (int i = start; i < 990; i++) {
if ((in = _vm->_saveFileMan->openForLoading(_vm->getSavegameFilename(i))))
diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp
index 2169e5283f..c224a76385 100644
--- a/engines/kyra/kyra_mr.cpp
+++ b/engines/kyra/kyra_mr.cpp
@@ -654,8 +654,6 @@ void KyraEngine_MR::startup() {
_invWsa->open("MOODOMTR.WSA", 1, 0);
_invWsaFrame = 6;
saveGameState(0, "New Game", 0);
- _soundDigital->beginFadeOut(_musicSoundChannel, 60);
- delayWithTicks(60);
if (_gameToLoad == -1)
enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1);
else
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 1c27716a67..4d0248b1e4 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -179,7 +179,7 @@ Common::Error KyraEngine_v1::init() {
#ifdef ENABLE_LOL
_flags.gameID = GI_LOL;
#else
- error("Lands of Lore demo is not supported in this build.");
+ error("Lands of Lore demo is not supported in this build");
#endif // !ENABLE_LOL
}
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index f05e113456..d077d3a3b0 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -387,6 +387,7 @@ protected:
bool canSaveGameStateCurrently() { return _isSaveAllowed; }
const char *getSavegameFilename(int num);
+ Common::String _savegameFilename;
static Common::String getSavegameFilename(const Common::String &target, int num);
bool saveFileLoadable(int slot);
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
index efc1b16c9b..63b8072654 100644
--- a/engines/kyra/resource.cpp
+++ b/engines/kyra/resource.cpp
@@ -38,6 +38,11 @@ namespace Kyra {
Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(), _protectedFiles(), _loaders(), _vm(vm) {
initializeLoaders();
+ // Initialize directories for playing from CD or with original
+ // directory structure
+ if (_vm->game() == GI_KYRA3)
+ SearchMan.addSubDirectoryMatching(Common::FSNode(ConfMan.get("path")), "malcolm");
+
_files.add("global_search", &Common::SearchManager::instance(), 3, false);
// compressed installer archives are added at level '2',
// but that's done in Resource::reset not here
@@ -70,7 +75,7 @@ bool Resource::reset() {
// List of files in the talkie version, which can never be unload.
static const char * const list[] = {
"ADL.PAK", "CHAPTER1.VRM", "COL.PAK", "FINALE.PAK", "INTRO1.PAK", "INTRO2.PAK",
- "INTRO3.PAK", "INTRO4.PAK", "MISC.PAK", "SND.PAK", "STARTUP.PAK", "XMI.PAK",
+ "INTRO3.PAK", "INTRO4.PAK", "MISC.PAK", "SND.PAK", "STARTUP.PAK", "XMI.PAK",
"CAVE.APK", "DRAGON1.APK", "DRAGON2.APK", "LAGOON.APK", 0
};
@@ -148,7 +153,7 @@ bool Resource::loadPakFile(Common::String filename) {
return loadPakFile(filename, file);
}
-bool Resource::loadPakFile(Common::String name, Common::SharedPtr<Common::ArchiveMember> file) {
+bool Resource::loadPakFile(Common::String name, Common::ArchiveMemberPtr file) {
name.toUppercase();
if (_archiveFiles.hasArchive(name) || _protectedFiles.hasArchive(name))
@@ -314,7 +319,7 @@ Common::SeekableReadStream *Resource::createReadStream(const Common::String &fil
return _files.createReadStreamForMember(file);
}
-Common::Archive *Resource::loadArchive(const Common::String &name, Common::SharedPtr<Common::ArchiveMember> member) {
+Common::Archive *Resource::loadArchive(const Common::String &name, Common::ArchiveMemberPtr member) {
ArchiveMap::iterator cachedArchive = _archiveCache.find(name);
if (cachedArchive != _archiveCache.end())
return cachedArchive->_value;
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
index d572c1ac54..8372bf9ad1 100644
--- a/engines/kyra/resource.h
+++ b/engines/kyra/resource.h
@@ -55,7 +55,7 @@ public:
bool reset();
bool loadPakFile(Common::String filename);
- bool loadPakFile(Common::String name, Common::SharedPtr<Common::ArchiveMember> file);
+ bool loadPakFile(Common::String name, Common::ArchiveMemberPtr file);
void unloadPakFile(Common::String filename, bool remFromCache = false);
@@ -86,7 +86,7 @@ protected:
Common::SearchSet _archiveFiles;
Common::SearchSet _protectedFiles;
- Common::Archive *loadArchive(const Common::String &name, Common::SharedPtr<Common::ArchiveMember> member);
+ Common::Archive *loadArchive(const Common::String &name, Common::ArchiveMemberPtr member);
Common::Archive *loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset);
bool loadProtectedFiles(const char * const * list);
diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp
index 1021cface5..445ea579a0 100644
--- a/engines/kyra/resource_intern.cpp
+++ b/engines/kyra/resource_intern.cpp
@@ -35,16 +35,8 @@ namespace Kyra {
// -> PlainArchive implementation
-PlainArchive::PlainArchive(Common::SharedPtr<Common::ArchiveMember> file, const FileInputList &files)
+PlainArchive::PlainArchive(Common::ArchiveMemberPtr file)
: _file(file), _files() {
- for (FileInputList::const_iterator i = files.begin(); i != files.end(); ++i) {
- Entry entry;
-
- entry.offset = i->offset;
- entry.size = i->size;
-
- _files[i->name] = entry;
- }
}
bool PlainArchive::hasFile(const Common::String &name) {
@@ -81,6 +73,99 @@ Common::SeekableReadStream *PlainArchive::createReadStreamForMember(const Common
return new Common::SeekableSubReadStream(parent, fDesc->_value.offset, fDesc->_value.offset + fDesc->_value.size, DisposeAfterUse::YES);
}
+void PlainArchive::addFileEntry(const Common::String &name, const Entry entry) {
+ _files[name] = entry;
+}
+
+PlainArchive::Entry PlainArchive::getFileEntry(const Common::String &name) const {
+ FileMap::const_iterator fDesc = _files.find(name);
+ if (fDesc == _files.end())
+ return Entry();
+ return fDesc->_value;
+}
+
+// -> TlkArchive implementation
+
+TlkArchive::TlkArchive(Common::ArchiveMemberPtr file, uint16 entryCount, const uint32 *fileEntries)
+ : _file(file), _entryCount(entryCount), _fileEntries(fileEntries) {
+}
+
+TlkArchive::~TlkArchive() {
+ delete[] _fileEntries;
+}
+
+bool TlkArchive::hasFile(const Common::String &name) {
+ return (findFile(name) != 0);
+}
+
+int TlkArchive::listMembers(Common::ArchiveMemberList &list) {
+ uint count = 0;
+
+ for (; count < _entryCount; ++count) {
+ const Common::String name = Common::String::printf("%08u.AUD", _fileEntries[count * 2 + 0]);
+ list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(name, this)));
+ }
+
+ return count;
+}
+
+Common::ArchiveMemberPtr TlkArchive::getMember(const Common::String &name) {
+ if (!hasFile(name))
+ return Common::ArchiveMemberPtr();
+
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *TlkArchive::createReadStreamForMember(const Common::String &name) const {
+ const uint32 *fileDesc = findFile(name);
+ if (!fileDesc)
+ return 0;
+
+ Common::SeekableReadStream *parent = _file->createReadStream();
+ if (!parent)
+ return 0;
+
+ parent->seek(fileDesc[1], SEEK_SET);
+ const uint32 size = parent->readUint32LE();
+ const uint32 fileStart = fileDesc[1] + 4;
+
+ return new Common::SeekableSubReadStream(parent, fileStart, fileStart + size, DisposeAfterUse::YES);
+}
+
+const uint32 *TlkArchive::findFile(const Common::String &name) const {
+ Common::String uppercaseName = name;
+ uppercaseName.toUppercase();
+
+ if (!uppercaseName.hasSuffix(".AUD"))
+ return 0;
+
+ uint32 id;
+ if (sscanf(uppercaseName.c_str(), "%08u.AUD", &id) != 1)
+ return 0;
+
+ // Binary search for the file entry
+ int leftIndex = 0;
+ int rightIndex = _entryCount - 1;
+
+ while (leftIndex <= rightIndex) {
+ int mid = (leftIndex + rightIndex) / 2;
+
+ const uint32 key = _fileEntries[mid * 2];
+ if (key == id) {
+ // Found
+ return &_fileEntries[mid * 2];
+ } else if (key > id) {
+ // Take the left sub-tree
+ rightIndex = mid - 1;
+ } else {
+ // Take the right sub-tree
+ leftIndex = mid + 1;
+ }
+ }
+
+ return 0;
+}
+
// -> CachedArchive implementation
CachedArchive::CachedArchive(const FileInputList &files)
@@ -142,6 +227,20 @@ bool ResLoaderPak::checkFilename(Common::String filename) const {
return (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK") || filename.hasSuffix(".VRM") || filename.hasSuffix(".CMP") || filename.hasSuffix(".TLK") || filename.equalsIgnoreCase(StaticResource::staticDataFilename()));
}
+namespace {
+
+Common::String readString(Common::SeekableReadStream &stream) {
+ Common::String result;
+ char c = 0;
+
+ while ((c = stream.readByte()) != 0)
+ result += c;
+
+ return result;
+}
+
+} // end of anonymous namespace
+
bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {
int32 filesize = stream.size();
if (filesize < 0)
@@ -163,12 +262,7 @@ bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableRe
if (offset < stream.pos() || offset > filesize || offset < 0)
return false;
- byte c = 0;
-
- file.clear();
-
- while (!stream.eos() && (c = stream.readByte()) != 0)
- file += c;
+ file = readString(stream);
if (stream.eos())
return false;
@@ -191,34 +285,15 @@ bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableRe
return true;
}
-namespace {
-
-Common::String readString(Common::SeekableReadStream &stream) {
- Common::String result;
- char c = 0;
-
- while ((c = stream.readByte()) != 0)
- result += c;
-
- return result;
-}
-
-struct PlainArchiveListSearch {
- PlainArchiveListSearch(const Common::String &search) : _search(search) {}
-
- bool operator()(const PlainArchive::InputEntry &entry) {
- return _search.equalsIgnoreCase(entry.name);
- }
- Common::String _search;
-};
-
-} // end of anonymous namespace
-
-Common::Archive *ResLoaderPak::load(Common::SharedPtr<Common::ArchiveMember> memberFile, Common::SeekableReadStream &stream) const {
+Common::Archive *ResLoaderPak::load(Common::ArchiveMemberPtr memberFile, Common::SeekableReadStream &stream) const {
int32 filesize = stream.size();
if (filesize < 0)
return 0;
+ Common::ScopedPtr<PlainArchive> result(new PlainArchive(memberFile));
+ if (!result)
+ return 0;
+
int32 startoffset = 0, endoffset = 0;
bool switchEndian = false;
bool firstFile = true;
@@ -229,8 +304,6 @@ Common::Archive *ResLoaderPak::load(Common::SharedPtr<Common::ArchiveMember> mem
startoffset = SWAP_BYTES_32(startoffset);
}
- PlainArchive::FileInputList files;
-
Common::String file;
while (!stream.eos()) {
// The start offset of a file should never be in the filelist
@@ -239,11 +312,7 @@ Common::Archive *ResLoaderPak::load(Common::SharedPtr<Common::ArchiveMember> mem
return 0;
}
- file.clear();
- byte c = 0;
-
- while (!stream.eos() && (c = stream.readByte()) != 0)
- file += c;
+ file = readString(stream);
if (stream.eos()) {
warning("PAK file '%s' is corrupted", memberFile->getDisplayName().c_str());
@@ -271,14 +340,8 @@ Common::Archive *ResLoaderPak::load(Common::SharedPtr<Common::ArchiveMember> mem
if (!endoffset)
endoffset = filesize;
- if (startoffset != endoffset) {
- PlainArchive::InputEntry entry;
- entry.size = endoffset - startoffset;
- entry.offset = startoffset;
- entry.name = file;
-
- files.push_back(entry);
- }
+ if (startoffset != endoffset)
+ result->addFileEntry(file, PlainArchive::Entry(startoffset, endoffset - startoffset));
if (endoffset == filesize)
break;
@@ -286,38 +349,32 @@ Common::Archive *ResLoaderPak::load(Common::SharedPtr<Common::ArchiveMember> mem
startoffset = endoffset;
}
- PlainArchive::FileInputList::const_iterator iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch("LINKLIST"));
- if (iter != files.end()) {
- stream.seek(iter->offset, SEEK_SET);
+ PlainArchive::Entry linklistFile = result->getFileEntry("LINKLIST");
+ if (linklistFile.size != 0) {
+ stream.seek(linklistFile.offset, SEEK_SET);
- uint32 magic = stream.readUint32BE();
+ const uint32 magic = stream.readUint32BE();
if (magic != MKID_BE('SCVM'))
error("LINKLIST file does not contain 'SCVM' header");
- uint32 links = stream.readUint32BE();
- for (uint i = 0; i < links; ++i) {
- Common::String linksTo = readString(stream);
- uint32 sources = stream.readUint32BE();
+ const uint32 links = stream.readUint32BE();
+ for (uint32 i = 0; i < links; ++i) {
+ const Common::String linksTo = readString(stream);
+ const uint32 sources = stream.readUint32BE();
- iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo));
- if (iter == files.end())
+ PlainArchive::Entry destination = result->getFileEntry(linksTo);
+ if (destination.size == 0)
error("PAK file link destination '%s' not found", linksTo.c_str());
- for (uint j = 0; j < sources; ++j) {
- Common::String dest = readString(stream);
-
- PlainArchive::InputEntry link = *iter;
- link.name = dest;
- files.push_back(link);
-
- // Better safe than sorry, we update the 'iter' value, in case push_back invalidated it
- iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo));
+ for (uint32 j = 0; j < sources; ++j) {
+ const Common::String dest = readString(stream);
+ result->addFileEntry(dest, destination);
}
}
}
- return new PlainArchive(memberFile, files);
+ return result.release();
}
// -> ResLoaderInsMalcolm implementation
@@ -343,9 +400,11 @@ bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::See
return (buffer[0] == 0x0D && buffer[1] == 0x0A);
}
-Common::Archive *ResLoaderInsMalcolm::load(Common::SharedPtr<Common::ArchiveMember> memberFile, Common::SeekableReadStream &stream) const {
+Common::Archive *ResLoaderInsMalcolm::load(Common::ArchiveMemberPtr memberFile, Common::SeekableReadStream &stream) const {
Common::List<Common::String> filenames;
- PlainArchive::FileInputList files;
+ Common::ScopedPtr<PlainArchive> result(new PlainArchive(memberFile));
+ if (!result)
+ return 0;
// thanks to eriktorbjorn for this code (a bit modified though)
stream.seek(3, SEEK_SET);
@@ -374,17 +433,14 @@ Common::Archive *ResLoaderInsMalcolm::load(Common::SharedPtr<Common::ArchiveMemb
stream.seek(3, SEEK_SET);
for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) {
- PlainArchive::InputEntry entry;
- entry.size = stream.readUint32LE();
- entry.offset = stream.pos();
- entry.name = *file;
- entry.name.toLowercase();
- stream.seek(entry.size, SEEK_CUR);
-
- files.push_back(entry);
+ const uint32 fileSize = stream.readUint32LE();
+ const uint32 fileOffset = stream.pos();
+
+ result->addFileEntry(*file, PlainArchive::Entry(fileOffset, fileSize));
+ stream.seek(fileSize, SEEK_CUR);
}
- return new PlainArchive(memberFile, files);
+ return result.release();
}
bool ResLoaderTlk::checkFilename(Common::String filename) const {
@@ -412,31 +468,20 @@ bool ResLoaderTlk::isLoadable(const Common::String &filename, Common::SeekableRe
return true;
}
-Common::Archive *ResLoaderTlk::load(Common::SharedPtr<Common::ArchiveMember> file, Common::SeekableReadStream &stream) const {
- uint16 entries = stream.readUint16LE();
- PlainArchive::FileInputList files;
-
- for (uint i = 0; i < entries; ++i) {
- PlainArchive::InputEntry entry;
-
- uint32 resFilename = stream.readUint32LE();
- uint32 resOffset = stream.readUint32LE();
+Common::Archive *ResLoaderTlk::load(Common::ArchiveMemberPtr file, Common::SeekableReadStream &stream) const {
+ const uint16 entryCount = stream.readUint16LE();
- entry.offset = resOffset+4;
+ uint32 *fileEntries = new uint32[entryCount * 2];
+ assert(fileEntries);
+ stream.read(fileEntries, sizeof(uint32) * entryCount * 2);
- char realFilename[20];
- snprintf(realFilename, 20, "%.08u.AUD", resFilename);
- entry.name = realFilename;
-
- uint32 curOffset = stream.pos();
- stream.seek(resOffset, SEEK_SET);
- entry.size = stream.readUint32LE();
- stream.seek(curOffset, SEEK_SET);
-
- files.push_back(entry);
+ for (uint i = 0; i < entryCount; ++i) {
+ fileEntries[i * 2 + 0] = READ_LE_UINT32(&fileEntries[i * 2 + 0]);
+ fileEntries[i * 2 + 1] = READ_LE_UINT32(&fileEntries[i * 2 + 1]);
}
- return new PlainArchive(file, files);
+
+ return new TlkArchive(file, entryCount, fileEntries);
}
// InstallerLoader implementation
@@ -469,7 +514,7 @@ void FileExpanderSource::advSrcBitsBy1() {
_key >>= 1;
if (!--_bitsLeft) {
if (_dataPtr < _endofBuffer)
- _key = ((*_dataPtr++) << 8 ) | (_key & 0xff);
+ _key = ((*_dataPtr++) << 8) | (_key & 0xff);
_bitsLeft = 8;
}
}
diff --git a/engines/kyra/resource_intern.h b/engines/kyra/resource_intern.h
index bceccc34b7..16f3a1fe91 100644
--- a/engines/kyra/resource_intern.h
+++ b/engines/kyra/resource_intern.h
@@ -38,33 +38,49 @@ class Resource;
class PlainArchive : public Common::Archive {
public:
- struct InputEntry {
- Common::String name;
+ struct Entry {
+ Entry() : offset(0), size(0) {}
+ Entry(uint32 o, uint32 s) : offset(o), size(s) {}
uint32 offset;
uint32 size;
};
- typedef Common::List<InputEntry> FileInputList;
+ PlainArchive(Common::ArchiveMemberPtr file);
- PlainArchive(Common::SharedPtr<Common::ArchiveMember> file, const FileInputList &files);
+ void addFileEntry(const Common::String &name, const Entry entry);
+ Entry getFileEntry(const Common::String &name) const;
+ // Common::Archive API implementation
bool hasFile(const Common::String &name);
int listMembers(Common::ArchiveMemberList &list);
Common::ArchiveMemberPtr getMember(const Common::String &name);
Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
private:
- struct Entry {
- uint32 offset;
- uint32 size;
- };
-
typedef Common::HashMap<Common::String, Entry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
- Common::SharedPtr<Common::ArchiveMember> _file;
+ Common::ArchiveMemberPtr _file;
FileMap _files;
};
+class TlkArchive : public Common::Archive {
+public:
+ TlkArchive(Common::ArchiveMemberPtr file, uint16 entryCount, const uint32 *fileEntries);
+ ~TlkArchive();
+
+ bool hasFile(const Common::String &name);
+ int listMembers(Common::ArchiveMemberList &list);
+ Common::ArchiveMemberPtr getMember(const Common::String &name);
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+private:
+ Common::ArchiveMemberPtr _file;
+
+ const uint32 *findFile(const Common::String &name) const;
+
+ const uint16 _entryCount;
+ const uint32 * const _fileEntries;
+};
+
class CachedArchive : public Common::Archive {
public:
struct InputEntry {
@@ -99,28 +115,28 @@ public:
virtual ~ResArchiveLoader() {}
virtual bool checkFilename(Common::String filename) const = 0;
virtual bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const = 0;
- virtual Common::Archive *load(Common::SharedPtr<Common::ArchiveMember> file, Common::SeekableReadStream &stream) const = 0;
+ virtual Common::Archive *load(Common::ArchiveMemberPtr file, Common::SeekableReadStream &stream) const = 0;
};
class ResLoaderPak : public ResArchiveLoader {
public:
bool checkFilename(Common::String filename) const;
bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- Common::Archive *load(Common::SharedPtr<Common::ArchiveMember> file, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Common::ArchiveMemberPtr file, Common::SeekableReadStream &stream) const;
};
class ResLoaderInsMalcolm : public ResArchiveLoader {
public:
bool checkFilename(Common::String filename) const;
bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- Common::Archive *load(Common::SharedPtr<Common::ArchiveMember> file, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Common::ArchiveMemberPtr file, Common::SeekableReadStream &stream) const;
};
class ResLoaderTlk : public ResArchiveLoader {
public:
bool checkFilename(Common::String filename) const;
bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;
- Common::Archive *load(Common::SharedPtr<Common::ArchiveMember> file, Common::SeekableReadStream &stream) const;
+ Common::Archive *load(Common::ArchiveMemberPtr file, Common::SeekableReadStream &stream) const;
};
class InstallerLoader {
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
index 56e1c73d0a..3ad7093046 100644
--- a/engines/kyra/saveload.cpp
+++ b/engines/kyra/saveload.cpp
@@ -137,7 +137,7 @@ Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filena
kReadSaveHeaderError errorCode = KyraEngine_v1::readSaveHeader(in, false, header);
if (errorCode != kRSHENoError) {
if (errorCode == kRSHEInvalidType)
- warning("No ScummVM Kyra engine savefile header.");
+ warning("No ScummVM Kyra engine savefile header");
else if (errorCode == kRSHEInvalidVersion)
warning("Savegame is not the right version (%u, '%s')", header.version, header.oldHeader ? "true" : "false");
else if (errorCode == kRSHEIoError)
@@ -224,9 +224,8 @@ Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, con
}
const char *KyraEngine_v1::getSavegameFilename(int num) {
- static Common::String filename;
- filename = getSavegameFilename(_targetName, num);
- return filename.c_str();
+ _savegameFilename = getSavegameFilename(_targetName, num);
+ return _savegameFilename.c_str();
}
Common::String KyraEngine_v1::getSavegameFilename(const Common::String &target, int num) {
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
index ade9886c2e..b7e01f31aa 100644
--- a/engines/kyra/screen.cpp
+++ b/engines/kyra/screen.cpp
@@ -40,7 +40,7 @@ namespace Kyra {
Screen::Screen(KyraEngine_v1 *vm, OSystem *system)
: _system(system), _vm(vm), _sjisInvisibleColor(0),
- _cursorColorKey((vm->gameFlags().gameID == GI_KYRA1) ? 0xFF : 0x00) {
+ _cursorColorKey((vm->game() == GI_KYRA1) ? 0xFF : 0x00) {
_debugEnabled = false;
_maskMinY = _maskMaxY = -1;
@@ -86,7 +86,7 @@ bool Screen::init() {
if (_vm->gameFlags().useHiResOverlay) {
_useOverlays = true;
_useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
- _sjisInvisibleColor = (_vm->gameFlags().gameID == GI_KYRA1) ? 0x80 : 0xF6;
+ _sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6;
for (int i = 0; i < SCREEN_OVLS_NUM; ++i) {
if (!_sjisOverlayPtrs[i]) {
@@ -787,7 +787,7 @@ void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPag
}
if (y2 < 0) {
- if (y2 <= -h )
+ if (y2 <= -h)
return;
h += y2;
y1 -= y2;
@@ -1084,7 +1084,7 @@ void Screen::setTextColor(const uint8 *cmap, int a, int b) {
bool Screen::loadFont(FontId fontId, const char *filename) {
if (fontId == FID_SJIS_FNT) {
- warning("Trying to replace system SJIS font.");
+ warning("Trying to replace system SJIS font");
return true;
}
@@ -1300,7 +1300,7 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int
_dsScaleH = 0x100;
}
- if ((flags & 0x2000) && _vm->gameFlags().gameID != GI_KYRA1)
+ if ((flags & 0x2000) && _vm->game() != GI_KYRA1)
_dsTable5 = va_arg(args, uint8 *);
static const DsMarginSkipFunc dsMarginFunc[] = {
@@ -1436,7 +1436,7 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int
uint16 frameSize = READ_LE_UINT16(src); src += 2;
- int colorTableColors = ((_vm->gameFlags().gameID != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
+ int colorTableColors = ((_vm->game() != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
if (!(flags & 0x8000) && (shapeFlags & 1))
_dsTable2 = src;
@@ -3016,10 +3016,10 @@ byte *Screen::getOverlayPtr(int page) {
else if (page == 2 || page == 3)
return _sjisOverlayPtrs[2];
- if (_vm->gameFlags().gameID == GI_KYRA2) {
+ if (_vm->game() == GI_KYRA2) {
if (page == 12 || page == 13)
return _sjisOverlayPtrs[3];
- } else if (_vm->gameFlags().gameID == GI_LOL) {
+ } else if (_vm->game() == GI_LOL) {
if (page == 4 || page == 5)
return _sjisOverlayPtrs[3];
if (page == 6 || page == 7)
diff --git a/engines/kyra/screen_lol.cpp b/engines/kyra/screen_lol.cpp
index be3dbe5b21..e8ec7bc180 100644
--- a/engines/kyra/screen_lol.cpp
+++ b/engines/kyra/screen_lol.cpp
@@ -637,7 +637,7 @@ void Screen_LoL::copyRegionSpecial(int page1, int w1, int h1, int x1, int y1, in
d++;
}
- for (int ii = (i & 1) ^ 1; ii < ibw_2; ii += 2 ) {
+ for (int ii = (i & 1) ^ 1; ii < ibw_2; ii += 2) {
*d = *s;
d += 2;
s += 2;
diff --git a/engines/kyra/screen_v2.cpp b/engines/kyra/screen_v2.cpp
index 3907f844cb..4d90bf2bab 100644
--- a/engines/kyra/screen_v2.cpp
+++ b/engines/kyra/screen_v2.cpp
@@ -53,7 +53,7 @@ uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int opColor
int maxIndex = maxColor;
if (maxIndex == -1) {
- if (_vm->gameFlags().gameID == GI_LOL) {
+ if (_vm->game() == GI_LOL) {
if (_use16ColorMode)
maxIndex = 255;
else
@@ -64,9 +64,9 @@ uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int opColor
}
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);
+ const byte curR = pal[i * 3 + 0] - (((pal[i * 3 + 0] - opR) * weight) >> 7);
+ const byte curG = pal[i * 3 + 1] - (((pal[i * 3 + 1] - opG) * weight) >> 7);
+ const byte curB = pal[i * 3 + 2] - (((pal[i * 3 + 2] - opB) * weight) >> 7);
uint16 idxSum = _use16ColorMode ? 0xFFFF : 0x7FFF;
byte index = opColor;
diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp
index 20bc8abec5..61916b92c0 100644
--- a/engines/kyra/script_tim.cpp
+++ b/engines/kyra/script_tim.cpp
@@ -95,7 +95,7 @@ TIMInterpreter::TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSys
_textDisplayed = false;
_textAreaBuffer = new uint8[320*40];
assert(_textAreaBuffer);
- if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->gameFlags().gameID == GI_LOL)
+ if ((_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().isDemo) && _vm->game() == GI_LOL)
_drawPage2 = 0;
else
_drawPage2 = 8;
@@ -176,7 +176,7 @@ TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpc
Common::strlcpy(_tim->filename, filename, 13);
- _tim->isLoLOutro = (_vm->gameFlags().gameID == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM");
+ _tim->isLoLOutro = (_vm->game() == GI_LOL) && !scumm_stricmp(filename, "LOLFINAL.TIM");
_tim->lolCharacter = 0;
TIM *r = _tim;
@@ -259,7 +259,7 @@ int TIMInterpreter::exec(TIM *tim, bool loop) {
if (cur.ip) {
cur.ip += cur.ip[0];
cur.lastTime = cur.nextTime;
- cur.nextTime += (cur.ip[1] ) * _vm->tickLength();
+ cur.nextTime += cur.ip[1] * _vm->tickLength();
}
}
}
@@ -467,7 +467,7 @@ void TIMInterpreter::setupTextPalette(uint index, int fadePalette) {
int TIMInterpreter::initAnimStruct(int index, const char *filename, int x, int y, int, int offscreenBuffer, uint16 wsaFlags) {
Movie *wsa = 0;
- const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->gameFlags().gameID == GI_LOL;
+ const bool isLoLDemo = _vm->gameFlags().isDemo && _vm->game() == GI_LOL;
if (isLoLDemo || _vm->gameFlags().platform == Common::kPlatformPC98 || _currentTim->isLoLOutro)
_drawPage2 = 0;
@@ -755,7 +755,7 @@ int TIMInterpreter::cmd_loadSoundFile(const uint16 *param) {
const char *file = (const char *)(_currentTim->text + READ_LE_UINT16(_currentTim->text + (param[0]<<1)));
_vm->sound()->loadSoundFile(file);
- if (_vm->gameFlags().gameID == GI_LOL)
+ if (_vm->game() == GI_LOL)
_vm->sound()->loadSfxFile(file);
return 1;
diff --git a/engines/kyra/sequences_lol.cpp b/engines/kyra/sequences_lol.cpp
index dc1a0f4d15..ac9148e607 100644
--- a/engines/kyra/sequences_lol.cpp
+++ b/engines/kyra/sequences_lol.cpp
@@ -81,6 +81,13 @@ int LoLEngine::processPrologue() {
}
switch (selection) {
+ case -1:
+ // This is sent on RTL for example, if we would not have any
+ // special case for this the default path would call quitGame
+ // and thus make the next game launched from the launcher
+ // quit instantly.
+ break;
+
case 0: // New game
processSelection = 0;
break;
@@ -397,7 +404,8 @@ void LoLEngine::kingSelectionIntro() {
_screen->fprintStringIntro("%s", 8, y + i * 10, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(57 + i));
}
- _sound->voicePlay("KING01", &_speechHandle);
+ if (_flags.isTalkie)
+ _sound->voicePlay("KING01", &_speechHandle);
int index = 4;
while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelection == -1 && !shouldQuit() && !skipFlag()) {
@@ -441,7 +449,8 @@ void LoLEngine::kingSelectionReminder() {
_screen->fprintStringIntro("%s", 8, y + 10, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(63));
}
- _sound->voicePlay("KING02", &_speechHandle);
+ if (_flags.isTalkie)
+ _sound->voicePlay("KING02", &_speechHandle);
int index = 0;
while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelection == -1 && !shouldQuit() && index < 15) {
@@ -468,7 +477,8 @@ void LoLEngine::kingSelectionReminder() {
}
void LoLEngine::kingSelectionOutro() {
- _sound->voicePlay("KING03", &_speechHandle);
+ if (_flags.isTalkie)
+ _sound->voicePlay("KING03", &_speechHandle);
int index = 0;
while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && !shouldQuit() && !skipFlag()) {
@@ -627,7 +637,8 @@ void LoLEngine::selectionCharInfoIntro(char *file) {
if (speechEnabled() && !_sound->isVoicePresent(file))
break;
- _sound->voicePlay(file, &_speechHandle);
+ if (_flags.isTalkie)
+ _sound->voicePlay(file, &_speechHandle);
int i = 0;
while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelectionInfoResult == -1 && !shouldQuit()) {
diff --git a/engines/kyra/sound_adlib.cpp b/engines/kyra/sound_adlib.cpp
index 9ab4aa0053..8458d751de 100644
--- a/engines/kyra/sound_adlib.cpp
+++ b/engines/kyra/sound_adlib.cpp
@@ -2241,7 +2241,7 @@ const int SoundAdLibPC::_kyra1NumSoundTriggers = ARRAYSIZE(SoundAdLibPC::_kyra1S
SoundAdLibPC::SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer)
: Sound(vm, mixer), _driver(0), _trackEntries(), _soundDataPtr(0) {
memset(_trackEntries, 0, sizeof(_trackEntries));
- _v2 = (_vm->gameFlags().gameID == GI_KYRA2) || (_vm->gameFlags().gameID == GI_LOL && !_vm->gameFlags().isDemo);
+ _v2 = (_vm->game() == GI_KYRA2) || (_vm->game() == GI_LOL && !_vm->gameFlags().isDemo);
_driver = new AdLibDriver(mixer, _v2);
assert(_driver);
diff --git a/engines/kyra/sound_digital.cpp b/engines/kyra/sound_digital.cpp
index 5b5a548f4a..150bafbef3 100644
--- a/engines/kyra/sound_digital.cpp
+++ b/engines/kyra/sound_digital.cpp
@@ -158,7 +158,6 @@ AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _end
_outBufferOffset(0), _outBufferSize(0), _inBuffer(0), _inBufferSize(0) {
_rate = _stream->readUint16LE();
- _length = Audio::Timestamp(0, _rate);
_totalSize = _stream->readUint32LE();
// TODO?: add checks
@@ -167,6 +166,9 @@ AUDStream::AUDStream(Common::SeekableReadStream *stream) : _stream(stream), _end
_streamStart = stream->pos();
+ debugC(5, kDebugLevelSound, "AUD Info: rate: %d, totalSize: %d, flags: %d, type: %d, streamStart: %d", _rate, _totalSize, flags, type, _streamStart);
+
+ _length = Audio::Timestamp(0, _rate);
for (uint32 i = 0; i < _totalSize;) {
uint16 size = _stream->readUint16LE();
uint16 outSize = _stream->readUint16LE();
@@ -460,6 +462,7 @@ int SoundDigital::playSound(const char *filename, uint8 priority, Audio::Mixer::
Common::strlcpy(use->filename, filename, sizeof(use->filename));
use->priority = priority;
+ debugC(5, kDebugLevelSound, "playSound: \"%s\"", use->filename);
Audio::SeekableAudioStream *audioStream = _supportedCodecs[usedCodec].streamFunc(stream, DisposeAfterUse::YES);
if (!audioStream) {
warning("Couldn't create audio stream for file '%s'", filename);
diff --git a/engines/kyra/sound_midi.cpp b/engines/kyra/sound_midi.cpp
index 026c72de26..c24ce5a95b 100644
--- a/engines/kyra/sound_midi.cpp
+++ b/engines/kyra/sound_midi.cpp
@@ -134,10 +134,14 @@ MidiOutput::MidiOutput(OSystem *system, MidiDriver *output, bool isMT32, bool de
static const byte sysEx2[] = { 3, 4, 3, 4, 3, 4, 3, 4, 4 };
static const byte sysEx3[] = { 0, 3, 2 };
- sendSysEx(0x7F, 0x00, 0x00, sysEx1, 1);
- sendSysEx(0x10, 0x00, 0x0D, sysEx1, 9);
- sendSysEx(0x10, 0x00, 0x04, sysEx2, 9);
- sendSysEx(0x10, 0x00, 0x01, sysEx3, 3);
+ if (_isMT32) {
+ sendSysEx(0x7F, 0x00, 0x00, sysEx1, 1);
+ sendSysEx(0x10, 0x00, 0x0D, sysEx1, 9);
+ sendSysEx(0x10, 0x00, 0x04, sysEx2, 9);
+ sendSysEx(0x10, 0x00, 0x01, sysEx3, 3);
+ } else {
+ _output->sendGMReset();
+ }
memset(_channels, 0, sizeof(_channels));
for (int i = 0; i < 16; ++i) {
@@ -519,12 +523,12 @@ bool SoundMidiPC::init() {
if (_nativeMT32 && _type == kMidiMT32) {
const char *midiFile = 0;
const char *pakFile = 0;
- if (_vm->gameFlags().gameID == GI_KYRA1) {
+ if (_vm->game() == GI_KYRA1) {
midiFile = "INTRO";
- } else if (_vm->gameFlags().gameID == GI_KYRA2) {
+ } else if (_vm->game() == GI_KYRA2) {
midiFile = "HOF_SYX";
pakFile = "AUDIO.PAK";
- } else if (_vm->gameFlags().gameID == GI_LOL) {
+ } else if (_vm->game() == GI_LOL) {
midiFile = "LOREINTR";
if (_vm->gameFlags().isDemo) {
@@ -618,7 +622,7 @@ void SoundMidiPC::loadSoundFile(Common::String file) {
// Since KYRA1 uses the same file for SFX and Music
// we setup sfx to play from music file as well
- if (_vm->gameFlags().gameID == GI_KYRA1) {
+ if (_vm->game() == GI_KYRA1) {
for (int i = 0; i < 3; ++i) {
_output->setSoundSource(i+1);
_sfx[i]->loadMusic(_musicFile, fileSize);
@@ -631,7 +635,7 @@ void SoundMidiPC::loadSfxFile(Common::String file) {
Common::StackLock lock(_mutex);
// Kyrandia 1 doesn't use a special sfx file
- if (_vm->gameFlags().gameID == GI_KYRA1)
+ if (_vm->game() == GI_KYRA1)
return;
file = getFileName(file);
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index d27b075906..86a1eb228e 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -47,6 +47,7 @@ SoundTowns::SoundTowns(KyraEngine_v1 *vm, Audio::Mixer *mixer)
SoundTowns::~SoundTowns() {
g_system->getAudioCDManager()->stop();
haltTrack();
+ delete _driver;
delete[] _musicTrackData;
delete[] _sfxFileData;
}
@@ -585,13 +586,13 @@ 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";
int h = 0;
if (_currentSFX) {
- while (_mixer->isSoundHandleActive(_soundChannels[h]) && h < kNumChannelHandles)
+ while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h]))
h++;
if (h >= kNumChannelHandles)
return 0;
@@ -606,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;
@@ -646,8 +647,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle,
sfx[i] = cmd;
}
- _currentSFX = Audio::makeRawStream(sfx, outsize, 11025,
- Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
+ _currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h], _currentSFX);
if (handle)
*handle = _soundChannels[h];
diff --git a/engines/kyra/sprites.cpp b/engines/kyra/sprites.cpp
index 4fa12fd687..52689869c6 100644
--- a/engines/kyra/sprites.cpp
+++ b/engines/kyra/sprites.cpp
@@ -71,7 +71,7 @@ void Sprites::setupSceneAnims() {
if (_anims[i].script != 0) {
data = _anims[i].script;
- assert( READ_LE_UINT16(data) == 0xFF86 );
+ assert(READ_LE_UINT16(data) == 0xFF86);
data += 4;
_anims[i].disable = READ_LE_UINT16(data) != 0;
@@ -509,7 +509,7 @@ void Sprites::loadDat(const char *filename, SceneExits &exits) {
}
void Sprites::freeSceneShapes() {
- for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++ ) {
+ for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++) {
delete[] _sceneShapes[i];
_sceneShapes[i] = 0;
}
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
index 4b71b1d69d..a06b488c86 100644
--- a/engines/kyra/staticres.cpp
+++ b/engines/kyra/staticres.cpp
@@ -878,7 +878,7 @@ void KyraEngine_LoK::loadItems() {
_shapes[323] = 0;
- for (shape = 1; shape < 6; shape++ )
+ for (shape = 1; shape < 6; shape++)
_shapes[323 + shape] = _screen->encodeShape((shape - 1) * 32, 0, 32, 17, 0);
for (shape = 330; shape <= 334; shape++)
diff --git a/engines/kyra/text_hof.cpp b/engines/kyra/text_hof.cpp
index 9d20cdd51a..4c292b70db 100644
--- a/engines/kyra/text_hof.cpp
+++ b/engines/kyra/text_hof.cpp
@@ -544,7 +544,7 @@ void KyraEngine_HoF::processDialogue(int dlgOffset, int vocH, int csEntry) {
}
objectChat((const char *)_unkBuf500Bytes, 0, vocHi, vocLo);
} else {
- if (activeTimSequence != nextTimSequence ) {
+ if (activeTimSequence != nextTimSequence) {
if (activeTimSequence > -1) {
deinitTalkObject(activeTimSequence);
activeTimSequence = -1;
diff --git a/engines/kyra/text_lol.cpp b/engines/kyra/text_lol.cpp
index d5264be483..7f9531507c 100644
--- a/engines/kyra/text_lol.cpp
+++ b/engines/kyra/text_lol.cpp
@@ -204,10 +204,12 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script,
}
void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) {
- static uint8 textColors256[] = { 0xfe, 0xa2, 0x84, 0x97, 0x9F };
- static uint8 textColors16[] = { 0x33, 0xaa, 0x88, 0x55, 0x99 };
- static uint8 soundEffect[] = { 0x0B, 0x00, 0x2B, 0x1B, 0x00 };
+ static const uint8 textColors256[] = { 0xfe, 0xa2, 0x84, 0x97, 0x9F };
+ static const uint8 textColors16[] = { 0x33, 0xaa, 0x88, 0x55, 0x99 };
+ static const uint8 soundEffect[] = { 0x0B, 0x00, 0x2B, 0x1B, 0x00 };
+
const uint8 *textColors = _vm->gameFlags().use16ColorMode ? textColors16 : textColors256;
+
if (type & 4)
type ^= 4;
else
diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp
index a84a84b51f..30b6f840fc 100644
--- a/engines/lure/hotspots.cpp
+++ b/engines/lure/hotspots.cpp
@@ -3133,8 +3133,14 @@ void HotspotTickHandlers::followerAnimHandler(Hotspot &h) {
const RoomTranslationRecord *p = &roomTranslations[0];
while ((p->srcRoom != 0) && (p->srcRoom != player->roomNumber()))
++p;
- h.currentActions().addFront(DISPATCH_ACTION,
- (p->srcRoom != 0) ? p->destRoom : player->roomNumber());
+
+ if (p->destRoom == h.roomNumber())
+ // Character is already in destination room, so set a random dest
+ h.setRandomDest();
+ else
+ // Move character to either the player's room, or found alternate destination
+ h.currentActions().addFront(DISPATCH_ACTION,
+ (p->srcRoom != 0) ? p->destRoom : player->roomNumber());
}
}
}
@@ -4032,12 +4038,14 @@ void HotspotTickHandlers::npcRoomChange(Hotspot &h) {
if (!h.currentActions().isEmpty()) {
if (h.startRoomNumber() != 0) {
- // If character isn't already returning to starting room, start them doing so
+ // If character isn't already returning to starting room, redirect them to the
+ // player's current room
if (!h.currentActions().bottom().hasSupportData() ||
(h.currentActions().bottom().supportData().action() != RETURN)) {
// Start follower returning
+ Hotspot *playerHotspot = res.getActiveHotspot(PLAYER_ID);
h.currentActions().clear();
- h.currentActions().addFront(RETURN, h.startRoomNumber(), 0, 0);
+ h.currentActions().addFront(RETURN, playerHotspot->roomNumber(), 0, 0);
}
}
diff --git a/engines/lure/hotspots.h b/engines/lure/hotspots.h
index 83c2e76c0e..5ff0ec563f 100644
--- a/engines/lure/hotspots.h
+++ b/engines/lure/hotspots.h
@@ -236,7 +236,6 @@ private:
BarPlaceResult getBarPlace();
bool findClearBarPlace();
bool characterWalkingCheck(uint16 id);
- bool doorCloseCheck(uint16 doorId);
void resetDirection();
// Action set
@@ -450,6 +449,7 @@ public:
void updateMovement();
void updateMovement2(CharacterMode value);
void resetPosition();
+ bool doorCloseCheck(uint16 doorId);
void doAction();
void doAction(Action action, HotspotData *hotspot);
diff --git a/engines/lure/res.cpp b/engines/lure/res.cpp
index f8b29d4dd6..4342a1d6ad 100644
--- a/engines/lure/res.cpp
+++ b/engines/lure/res.cpp
@@ -625,12 +625,16 @@ Hotspot *Resources::activateHotspot(uint16 hotspotId) {
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId);
res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber);
}
- if ((hotspotId == GOEWIN_ID) && (hotspot->roomNumber() == 39))
+ if ((hotspotId == GOEWIN_ID) && (hotspot->roomNumber() == 39)) {
// WORKAROUND: When you re-join Goewin in the caves, clear her schedule. This may prevent a
// situation where you could close the left door, and she'd be permanently stuck trying to go
// the next room on the left, since her old schedule still had her following your old path
hotspot->currentActions().clear();
+ // Since she's no longer a follower, clear her start room field
+ hotspot->setStartRoomNumber(0);
+ }
+
// TODO: Figure out why there's a room set in the animation decode for a range of characters,
// particularly since it doesn't seem to match what happens in-game
/*
diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp
index 41a09e0d1d..20cbd328ce 100644
--- a/engines/lure/scripts.cpp
+++ b/engines/lure/scripts.cpp
@@ -901,6 +901,16 @@ uint16 Script::execute(uint16 startOffset) {
uint16 offset = startOffset;
bool breakFlag = false;
+ // WORKAROUND: Prevents the Weregate door closing prematurely
+ if (startOffset == 3941) {
+ Hotspot *goewinHotspot = r.getActiveHotspot(GOEWIN_ID);
+ if (!goewinHotspot->doorCloseCheck(10025)) {
+ // Goewin is still blocking the door, so reschedule the closing
+ r.delayList().add(1, startOffset, false);
+ return 0;
+ }
+ }
+
param = 0;
fields.setField(SEQUENCE_RESULT, 0);
@@ -1297,6 +1307,11 @@ bool HotspotScript::execute(Hotspot *h) {
default:
// Set the animation frame number
+
+ // WORKAROUND: In Lure English EGA, the apparatus in room #30 can be set with an invalid frame number
+ if ((h->hotspotId() == 1059) && (opcode >= h->numFrames()))
+ opcode = h->numFrames() - 1;
+
debugC(ERROR_DETAILED, kLureDebugScripts, "SET FRAME NUMBER = %d", opcode);
h->setFrameNumber(opcode);
diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp
index a75545c330..ca15ad8287 100644
--- a/engines/lure/sound.cpp
+++ b/engines/lure/sound.cpp
@@ -63,8 +63,12 @@ SoundManager::SoundManager() {
_driver = NULL;
} else {
- if (_nativeMT32)
+ if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ _driver->sendMT32Reset();
+ } else {
+ _driver->sendGMReset();
+ }
for (index = 0; index < NUM_CHANNELS; ++index) {
_channelsInner[index].midiChannel = _driver->allocateChannel();
diff --git a/engines/m4/globals.h b/engines/m4/globals.h
index 3fc31b4ec2..67dcfc2b94 100644
--- a/engines/m4/globals.h
+++ b/engines/m4/globals.h
@@ -229,7 +229,68 @@ struct MadsConfigData {
#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;
+union DataMapEntry {
+ bool *boolValue;
+ uint16 *uint16Value;
+ int *intValue;
+};
+
+typedef Common::HashMap<uint16, uint16> DataMapHash;
+
+enum DataMapType {BOOL, UINT16, INT};
+
+class DataMapWrapper {
+ friend class DataMap;
+private:
+ DataMapEntry _value;
+ DataMapType _type;
+public:
+ DataMapWrapper(bool *v) { _value.boolValue = v; _type = BOOL; }
+ DataMapWrapper(uint16 *v) { _value.uint16Value = v; _type = UINT16; }
+ DataMapWrapper(int16 *v) { _value.uint16Value = (uint16 *)v; _type = UINT16; }
+ DataMapWrapper(int *v) { _value.intValue = v; _type = INT; }
+
+ uint16 getIntValue() {
+ if (_type == BOOL) return *_value.boolValue ? 0xffff : 0;
+ else if (_type == UINT16) return *_value.uint16Value;
+ else return *_value.intValue;
+ }
+ void setIntValue(uint16 v) {
+ if (_type == BOOL) *_value.boolValue = v != 0;
+ else if (_type == UINT16) *_value.uint16Value = v;
+ else *_value.intValue = v;
+ }
+};
+
+#define MAP_DATA(V) _madsVm->globals()->_dataMap.addMapping(new DataMapWrapper(V))
+
+class DataMap {
+private:
+ DataMapHash _data;
+ Common::Array<DataMapWrapper *> _mapList;
+public:
+ DataMap() {
+ _mapList.push_back(NULL);
+ }
+ ~DataMap() {
+ for (uint i = 1; i < _mapList.size(); ++i)
+ delete _mapList[i];
+ }
+
+ void addMapping(DataMapWrapper *v) { _mapList.push_back(v); }
+ uint16 get(uint16 index) {
+ if (index < _mapList.size())
+ return _mapList[index]->getIntValue();
+
+ return _data[index];
+ }
+ void set(uint16 index, uint16 v) {
+ if (index < _mapList.size())
+ _mapList[index]->setIntValue(v);
+ else
+ _data[index] = v;
+ }
+};
class MadsGlobals : public Globals {
private:
@@ -259,7 +320,7 @@ public:
int previousScene;
int16 _nextSceneId;
uint16 actionNouns[3];
- IntStorage _dataMap;
+ DataMap _dataMap;
int _difficultyLevel;
void loadMadsVocab();
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index 3174c886d5..b68f7248af 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -96,10 +96,10 @@ class Animation;
enum M4GameType {
GType_Riddle = 1,
- GType_Burger,
- GType_RexNebular,
- GType_DragonSphere,
- GType_Phantom
+ GType_Burger = 2,
+ GType_RexNebular = 3,
+ GType_DragonSphere = 4,
+ GType_Phantom = 5
};
enum Features {
@@ -224,8 +224,10 @@ public:
MadsGlobals *globals() { return (MadsGlobals *)_globals; }
MadsScene *scene() { return (MadsScene *)_scene; }
void startScene(int sceneNum) {
- if (!_scene)
+ if (!_scene) {
_scene = new MadsScene(this);
+ ((MadsScene *)_scene)->initialise();
+ }
_scene->show();
_scene->loadScene(101);
}
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index e7c20b237d..cebe2215ca 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -24,9 +24,12 @@
*/
#include "m4/m4.h"
+#include "m4/dialogs.h"
#include "m4/mads_logic.h"
#include "m4/scene.h"
+#define MAX_CALL_PARAMS 10
+
namespace M4 {
void MadsGameLogic::initialiseGlobals() {
@@ -139,6 +142,53 @@ void MadsGameLogic::initialiseGlobals() {
/*--------------------------------------------------------------------------*/
+const char *MadsSceneLogic::subFormatList[] = {"scene%d_enter", "scene%d_step", "scene%d_preaction", "scene%d_actions"};
+
+#define OPSIZE8 0x40 ///< when this bit is set - the operand size is 8 bits
+#define OPSIZE16 0x80 ///< when this bit is set - the operand size is 16 bits
+#define OPMASK 0x3F ///< mask to isolate the opcode
+
+enum Opcodes {
+ OP_HALT = 0, OP_IMM = 1, OP_ZERO = 2, OP_ONE = 3, OP_MINUSONE = 4, OP_STR = 5, OP_DLOAD = 6,
+ OP_DSTORE = 7, OP_PAL = 8, OP_LOAD = 9, OP_GLOAD = 10, OP_STORE = 11, OP_GSTORE = 12,
+ OP_CALL = 13, OP_LIBCALL = 14, OP_RET = 15, OP_ALLOC = 16, OP_JUMP = 17, OP_JMPFALSE = 18,
+ OP_JMPTRUE = 19, OP_EQUAL = 20, OP_LESS = 21, OP_LEQUAL = 22, OP_NEQUAL = 23, OP_GEQUAL = 24,
+ OP_GREAT = 25, OP_PLUS = 26, OP_MINUS = 27, OP_LOR = 28, OP_MULT = 29, OP_DIV = 30,
+ OP_MOD = 31, OP_AND = 32, OP_OR = 33, OP_EOR = 34, OP_LAND = 35, OP_NOT = 36, OP_COMP = 37,
+ OP_NEG = 38, OP_DUP = 39,
+ TOTAL_OPCODES = 40
+};
+
+const char *MadsSceneLogic::_opcodeStrings[] = {
+ "HALT", "IMM", "ZERO", "ONE", "MINUSONE", "STR", "DLOAD", "DSTORE", NULL, "LOAD", "GLOAD",
+ "STORE", "GSTORE", "CALL", "LIBCALL", "RET", "ALLOC", "JUMP", "JMPFALSE", "JMPTRUE", "EQUAL",
+ "LESS", "LEQUAL", "NEQUAL", "GEQUAL", "GREAT", "PLUS", "MINUS", "LOR", "MULT", "DIV",
+ "MOD", "AND", "OR", "EOR", "LAND", "NOT", "COMP", "NEG", "DUP"
+};
+
+/**
+ * This method sets up the data map with pointers to all the common game objects. This allows the script engine to
+ * convert game specific offsets for various fields in the original game's data segment into a generic data index
+ * that will be common across all the MADS games
+ */
+void MadsSceneLogic::initialiseDataMap() {
+ // The unique order of these items must be maintained
+ MAP_DATA((uint16 *)&_madsVm->scene()->_abortTimersMode2);
+ MAP_DATA(&_madsVm->scene()->_abortTimers);
+ MAP_DATA(&_madsVm->_player._stepEnabled);
+ MAP_DATA(&_madsVm->scene()->_nextScene);
+ MAP_DATA(&_madsVm->scene()->_previousScene);
+ MAP_DATA(&_madsVm->_player._playerPos.x);
+ MAP_DATA(&_madsVm->_player._playerPos.y);
+ MAP_DATA(&_madsVm->_player._direction);
+ MAP_DATA(&_madsVm->_player._visible);
+ MAP_DATA(&_madsVm->scene()->_animActive);
+}
+
+DataMap &MadsSceneLogic::dataMap() {
+ return _madsVm->globals()->_dataMap;
+}
+
const char *MadsSceneLogic::formAnimName(char sepChar, int16 suffixNum) {
return MADSResourceManager::getResourceName(sepChar, _sceneNumber, EXTTYPE_NONE, NULL, suffixNum);
}
@@ -177,10 +227,6 @@ void MadsSceneLogic::getAnimName() {
strcpy(_madsVm->scene()->_aaName, newName);
}
-IntStorage &MadsSceneLogic::dataMap() {
- return _madsVm->globals()->_dataMap;
-}
-
/*--------------------------------------------------------------------------*/
uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
@@ -220,44 +266,6 @@ void MadsSceneLogic::activateHotspot(int idx, bool active) {
// TODO:
}
-void MadsSceneLogic::lowRoomsEntrySound() {
- if (!_madsVm->globals()->_config.musicFlag) {
- _madsVm->_sound->playSound(2);
- } else {
- // Play different sounds for each of the rooms
- switch (_madsVm->globals()->sceneNumber) {
- case 101:
- _madsVm->_sound->playSound(11);
- break;
- case 102:
- _madsVm->_sound->playSound(12);
- break;
- case 103:
- _madsVm->_sound->playSound(3);
- _madsVm->_sound->playSound(25);
- break;
- case 104:
- _madsVm->_sound->playSound(10);
- break;
- case 105:
- if ((_madsVm->globals()->previousScene < 104) || (_madsVm->globals()->previousScene > 108))
- _madsVm->_sound->playSound(10);
- break;
- case 106:
- _madsVm->_sound->playSound(13);
- break;
- case 107:
- _madsVm->_sound->playSound(3);
- break;
- case 108:
- _madsVm->_sound->playSound(15);
- break;
- default:
- break;
- }
- }
-}
-
void MadsSceneLogic::getPlayerSpritesPrefix() {
_madsVm->_sound->playSound(5);
@@ -315,17 +323,107 @@ void MadsSceneLogic::getPlayerSpritesPrefix2() {
/*--------------------------------------------------------------------------*/
/**
- * FIXME:
- * Currently I'm only working at providing manual implementation of the first Rex Nebular scene.
- * It will make more sense to convert the remaining game logic from the games into some
- * kind of bytecode scripts
+ * Loads the MADS.DAT file and loads the script data for the correct game/language
*/
+void MadsSceneLogic::initialiseScripts() {
+ Common::File f;
+ if (!f.open("mads.dat")) {
+ warning("Could not locate mads.dat file");
+ return;
+ }
+
+ // Validate that the file being read is a valid mads.dat file
+ char header[4];
+ f.read(&header[0], 4);
+ if (strncmp(header, "MADS", 4) != 0) {
+ warning("Invalid mads.dat file");
+ return;
+ }
+
+ // Get a list of the offsets of game blocks
+ uint32 v;
+ Common::Array<uint32> offsets;
+ while ((v = f.readUint32LE()) != 0)
+ offsets.push_back(v);
+
+ // Check the header of each block in turn
+ _scriptsData = NULL;
+ _scriptsSize = 0;
+
+ for (uint i = 0; i < offsets.size(); ++i) {
+ // Get the block header
+ f.seek(offsets[i]);
+ byte gameId = f.readByte();
+ byte language = f.readByte();
+ f.readByte(); // Language currently unused
+
+ // If this block isn't for the current game, skip it
+ if (_madsVm->getGameType() != (gameId + 2))
+ continue;
+ if ((language != 1) || (_madsVm->getLanguage() != Common::EN_ANY))
+ continue;
+
+ // Found script block for the given game and language.
+ _scriptsSize = (i < (offsets.size() - 1)) ? offsets[i + 1] - offsets[i] : f.size() - offsets[i];
+ break;
+ }
+
+ if (!_scriptsSize) {
+ warning("Could not find appropriate scripts block for game in mads.dat file");
+ f.close();
+ return;
+ }
+
+ // Load up the list of subroutines into a hash map
+ uint32 blockOffset = f.pos() - 3;
+ uint32 subsStart = 0;
+ for (;;) {
+ // Get next entry
+ Common::String subName;
+ char c;
+ while ((c = (char)f.readByte()) != '\0')
+ subName += c;
+ if (subName.empty())
+ // Reached end of subroutine list
+ break;
+
+ // Read in the offset of the routine
+ uint32 offset = f.readUint32LE();
+ if (_subroutines.empty()) {
+ // The first subroutine offset is used to reduce the amount of data to later load in. In essence,
+ // the subroutine index will not be separately loaded, since it's contents will be in the hash map
+ subsStart = offset;
+ _scriptsSize -= offset;
+ }
+
+ _subroutines[subName] = offset - subsStart;
+ _subroutineOffsets.push_back(offset - subsStart);
+ }
+
+ // Read in the remaining data
+ f.seek(blockOffset + subsStart, SEEK_SET);
+ _scriptsData = (byte *)malloc(_scriptsSize);
+ f.read(_scriptsData, _scriptsSize);
+
+ f.close();
+}
void MadsSceneLogic::selectScene(int sceneNum) {
assert(sceneNum == 101);
_sceneNumber = sceneNum;
Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0);
+
+ // If debugging is turned on, show a debug warning if any of the scene methods aren't present
+ if (gDebugLevel > 0) {
+ for (int i = 0; i < 4; ++i) {
+ char buffer[20];
+ sprintf(buffer, subFormatList[i], sceneNum);
+ Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(Common::String(buffer));
+ if (it == _subroutines.end())
+ debugC(1, kDebugScript, "Scene method %s not found", buffer);
+ }
+ }
}
void MadsSceneLogic::setupScene() {
@@ -343,149 +441,492 @@ void MadsSceneLogic::setupScene() {
getAnimName();
}
-void MadsSceneLogic::enterScene() {
- for (int i = 1; i <= 7; ++i)
- _spriteIndexes[i - 1] = loadSpriteSet(i, 'x');
- _spriteIndexes[7] = loadSpriteSet(0xFFFF, 'm');
- _spriteIndexes[8] = loadSpriteSet(1, 'b');
- _spriteIndexes[9] = loadSpriteSet(2, 'b');
- _spriteIndexes[10] = loadSpriteSet(0, 'a');
- _spriteIndexes[11] = loadSpriteSet(1, 'a');
- _spriteIndexes[12] = loadSpriteSet(8, 'x');
- _spriteIndexes[13] = loadSpriteSet(0, 'x');
-
- _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);
-
- _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
-
- _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;
-
- if (_madsVm->globals()->previousScene != -1)
- _madsVm->globals()->_globals[10] = 0;
- if (_madsVm->globals()->previousScene != -2) {
- _madsVm->_player._playerPos = Common::Point(100, 152);
- }
-
- 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));
+/**
+ * Handles the logic when a scene is entered
+ */
+void MadsSceneLogic::doEnterScene() {
+ char buffer[20];
+ sprintf(buffer, subFormatList[SUBFORMAT_ENTER], _sceneNumber);
+ execute(Common::String(buffer));
+}
+
+/**
+ * Handles the script execution which is called regularly every frame
+ */
+void MadsSceneLogic::doSceneStep() {
+ char buffer[20];
+ sprintf(buffer, subFormatList[SUBFORMAT_STEP], _sceneNumber);
+ execute(Common::String(buffer));
+}
+
+/**
+ * Handles and preactions before an action is started
+ */
+void MadsSceneLogic::doPreactions() {
+ char buffer[20];
+ sprintf(buffer, subFormatList[SUBFORMAT_PREACTIONS], _sceneNumber);
+ execute(Common::String(buffer));
+}
+
+/**
+ * Handles any action that has been selected
+ */
+void MadsSceneLogic::doAction() {
+ char buffer[20];
+ sprintf(buffer, subFormatList[SUBFORMAT_ACTIONS], _sceneNumber);
+ execute(Common::String(buffer));
+}
+
+/**
+ * Executes the script with the specified name
+ */
+void MadsSceneLogic::execute(const Common::String &scriptName) {
+ Common::HashMap<Common::String, uint32>::iterator it = _subroutines.find(scriptName);
+ if (it != _subroutines.end())
+ execute(it->_value);
+}
+
+#define UNUSED_VAL 0xEAEAEAEA
+/**
+ * Executes the script at the specified offset
+ */
+void MadsSceneLogic::execute(uint32 subOffset) {
+ Common::Array<ScriptVar> locals;
+ Common::Stack<ScriptVar> stack;
+ char opcodeBuffer[100];
+ uint32 scriptOffset = subOffset;
+ uint32 param;
+
+ debugC(1, kDebugScript, "executing script at %xh", subOffset);
+
+ bool done = false;
+ while (!done) {
+ param = UNUSED_VAL;
+ byte opcode = _scriptsData[scriptOffset++];
+ sprintf(opcodeBuffer, "%.4x[%.2d] - %s", scriptOffset - 1, stack.size(), _opcodeStrings[opcode & OPMASK]);
+
+ switch (opcode & OPMASK) {
+ case OP_HALT: // end of program
+ case OP_RET:
+ done = true;
+ break;
+
+ case OP_IMM: // Loads immediate value onto stack
+ param = getParam(scriptOffset, opcode);
+ stack.push(ScriptVar(param));
+ break;
+
+ case OP_ZERO: // loads zero onto stack
+ stack.push(ScriptVar((uint32)0));
+ break;
+
+ case OP_ONE: // loads one onto stack
+ stack.push(ScriptVar(1));
+ break;
+
+ case OP_MINUSONE: // loads minus one (0xffff) onto stack
+ stack.push(ScriptVar(0xffff));
+ break;
+
+ case OP_DLOAD: { // Gets data variable
+ param = getParam(scriptOffset, opcode);
+ uint16 v = dataMap().get(param);
+ stack.push(ScriptVar(v));
+ break;
+ }
+
+ case OP_DSTORE: { // Stores data variable
+ param = getParam(scriptOffset, opcode);
+ ScriptVar v = stack.pop();
+ dataMap().set(param, v.isInt() ? v.get() : 0);
+ break;
+ }
- //if (_madsVm->globals()->previousScene == 112)
- // room101Check();
- } else {
- _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], false, 6, 0, 0, 0);
- _madsVm->scene()->_sequenceList.setDepth(_spriteIndexes[26], 4);
- }
+ case OP_LOAD: // loads local variable onto stack
+ param = getParam(scriptOffset, opcode);
+ stack.push(locals[param]);
+ break;
- _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1);
+ case OP_STORE: // Pops a value and stores it in a local
+ // Get the local index and expand the locals store if necessary
+ param = getParam(scriptOffset, opcode);
+ while (param >= locals.size())
+ locals.push_back(ScriptVar());
- if (_madsVm->globals()->_globals[10]) {
- const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1);
- _madsVm->scene()->loadAnimation(animName, 71);
+ locals[param] = stack.pop();
+ break;
- _madsVm->_player._playerPos = Common::Point(68, 140);
- _madsVm->_player._direction = 4;
- _madsVm->_player._visible = false;
- _madsVm->_player._stepEnabled = false;
+ case OP_GLOAD: // loads global variable onto stack
+ param = getParam(scriptOffset, opcode);
+ assert(param < TOTAL_NUM_VARIABLES);
+ stack.push(_madsVm->globals()->_globals[param]);
+ break;
+
+ case OP_GSTORE: // pops stack and stores in global variable
+ param = getParam(scriptOffset, opcode);
+ assert(param < TOTAL_NUM_VARIABLES);
+ _madsVm->globals()->_globals[param] = stack.pop().get();
+ break;
+
+ case OP_CALL: // procedure call
+ param = getParam(scriptOffset, opcode);
+ assert(param < _subroutineOffsets.size());
+ execute(_subroutineOffsets[param]);
+ break;
- dataMap()[0x56FC] = 0;
- dataMap()[0x5482] = 0;
- dataMap()[0x5484] = 30;
+ case OP_LIBCALL: // library procedure or function call
+ param = getParam(scriptOffset, opcode);
+ callSubroutine(param, stack);
+ break;
+
+ case OP_JUMP: // unconditional jump
+ param = subOffset + getParam(scriptOffset, opcode);
+ scriptOffset = param;
+ break;
+
+ case OP_JMPFALSE: // conditional jump
+ param = subOffset + getParam(scriptOffset, opcode);
+ if (stack.pop().get() == 0)
+ // Condition satisfied - do the jump
+ scriptOffset = param;
+ break;
+
+ case OP_JMPTRUE: // conditional jump
+ param = subOffset + getParam(scriptOffset, opcode);
+ if (stack.pop().get() != 0)
+ // Condition satisfied - do the jump
+ scriptOffset = param;
+ break;
+
+ case OP_EQUAL: // tests top two items on stack for equality
+ case OP_LESS: // tests top two items on stack
+ case OP_LEQUAL: // tests top two items on stack
+ case OP_NEQUAL: // tests top two items on stack
+ case OP_GEQUAL: // tests top two items on stack
+ case OP_GREAT: // tests top two items on stack
+ case OP_LOR: // logical or of top two items on stack and replaces with result
+ case OP_LAND: // logical ands top two items on stack and replaces with result
+ {
+ uint32 param2 = stack.pop().get();
+ uint32 param1 = stack.pop().get();
+
+ // Do the comparison
+ uint32 tmp = 0;
+ switch (opcode) {
+ case OP_EQUAL: tmp = (param1 == param2); break;
+ case OP_LESS: tmp = (param1 < param2); break;
+ case OP_LEQUAL: tmp = (param1 <= param2); break;
+ case OP_NEQUAL: tmp = (param1 != param2); break;
+ case OP_GEQUAL: tmp = (param1 >= param2); break;
+ case OP_GREAT: tmp = (param1 > param2); break;
+
+ case OP_LOR: tmp = (param1 || param2); break;
+ case OP_LAND: tmp = (param1 && param2); break;
+ }
+
+ stack.push(ScriptVar(tmp));
+ }
+ break;
+
+ case OP_PLUS: // adds top two items on stack and replaces with result
+ case OP_MINUS: // subs top two items on stack and replaces with result
+ case OP_MULT: // multiplies top two items on stack and replaces with result
+ case OP_DIV: // divides top two items on stack and replaces with result
+ case OP_MOD: // divides top two items on stack and replaces with modulus
+ case OP_AND: // bitwise ands top two items on stack and replaces with result
+ case OP_OR: // bitwise ors top two items on stack and replaces with result
+ case OP_EOR: // bitwise exclusive ors top two items on stack and replaces with result
+ {
+ uint32 param2 = stack.pop().get();
+ uint32 param1 = stack.pop().get();
+
+ // replace other operand with result of operation
+ switch (opcode) {
+ case OP_PLUS: param1 += param2; break;
+ case OP_MINUS: param1 -= param2; break;
+ case OP_MULT: param1 *= param2; break;
+ case OP_DIV: param1 /= param2; break;
+ case OP_MOD: param1 %= param2; break;
+ case OP_AND: param1 &= param2; break;
+ case OP_OR: param1 |= param2; break;
+ case OP_EOR: param1 ^= param2; break;
+ }
+
+ stack.push(ScriptVar(param1));
+ }
+ break;
+
+ case OP_NOT: // logical nots top item on stack
+ param = stack.pop().get();
+ stack.push(ScriptVar((uint32)!param & 0xffff));
+ break;
+
+ case OP_COMP: // complements top item on stack
+ param = stack.pop().get();
+ stack.push(ScriptVar(~param & 0xffff));
+ break;
+
+ case OP_NEG: // negates top item on stack
+ param = stack.pop().get();
+ stack.push(ScriptVar(((uint32)-(int32)param) & 0xffff));
+ break;
+
+ case OP_DUP: // duplicates top item on stack
+ stack.push(stack.top());
+ break;
+
+ default:
+ error("execute() - Unknown opcode");
+ }
+
+ // check for stack size
+ assert(stack.size() < 100);
+
+ if (gDebugLevel > 0) {
+ if (param != UNUSED_VAL)
+ sprintf(opcodeBuffer + strlen(opcodeBuffer), "\t%d", param);
+ debugC(2, kDebugScript, "%s", opcodeBuffer);
+ }
}
- _madsVm->globals()->_dataMap[0x5486] = 0;
- lowRoomsEntrySound();
+ debugC(1, kDebugScript, "finished executing script");
+
+ // make sure stack is unwound
+ assert(stack.size() == 0);
}
-void MadsSceneLogic::doPreactions() {
- warning("Still to do preactions logic");
+uint32 MadsSceneLogic::getParam(uint32 &scriptOffset, int opcode) {
+ switch (opcode & (~OPMASK)) {
+ case OPSIZE8:
+ return _scriptsData[scriptOffset++];
+ case OPSIZE16: {
+ uint16 v = READ_LE_UINT16(&_scriptsData[scriptOffset]);
+ scriptOffset += sizeof(uint16);
+ return v;
+ }
+ default: {
+ uint32 v = READ_LE_UINT32(&_scriptsData[scriptOffset]);
+ scriptOffset += sizeof(uint32);
+ return v;
+ }
+ }
}
-void MadsSceneLogic::doAction() {
- warning("Still to do actions logic");
+/**
+ * Support method for extracting the required number of parameters needed for a library routine call
+ */
+void MadsSceneLogic::getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams) {
+ assert(numParams <= MAX_CALL_PARAMS);
+ for (int i = 0; i < numParams; ++i, ++callParams)
+ *callParams = stack.pop();
}
-void MadsSceneLogic::doSceneStep() {
- // TODO: Sound handling
-
- switch (_madsVm->scene()->_abortTimers) {
- case 70:
- _madsVm->_sound->playSound(9);
+#define EXTRACT_PARAMS(n) getCallParameters(n, stack, p)
+
+void MadsSceneLogic::callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack) {
+ ScriptVar p[MAX_CALL_PARAMS];
+
+ switch (subIndex) {
+ case 1: {
+ // dialog_show
+ EXTRACT_PARAMS(1);
+ Dialog *dlg = new Dialog(_vm, p[0].getStr(), "TODO: Proper Title");
+ _vm->_viewManager->addView(dlg);
+ _vm->_viewManager->moveToFront(dlg);
break;
- case 71:
- _madsVm->globals()->_globals[10] = 0;
- _madsVm->_player._visible = true;
- _madsVm->_player._stepEnabled = true;
+ }
- _madsVm->_player._priorTimer = _madsVm->_currentTimer - _madsVm->_player._ticksAmount;
+ case 2:
+ // SequenceList_remove
+ EXTRACT_PARAMS(1);
+ _madsVm->scene()->_sequenceList.remove(p[0]);
break;
- case 72:
- case 73:
- // TODO: Method that should be scripted
+
+ case 3:
+ case 6:
+ case 20: {
+ // 3: start_reversible_sprite_sequence
+ // 6: start_cycled_sprite_sequence
+ // 20: start_sprite_sequence3
+ EXTRACT_PARAMS(6);
+ int idx;
+ if (subIndex == 3)
+ idx = startReversibleSpriteSequence(p[0], p[1] != 0, p[2], p[3], p[4], p[5]);
+ else if (subIndex == 6)
+ idx = startCycledSpriteSequence(p[0], p[1], p[2], p[3], p[4], p[5]);
+ else
+ idx = startSpriteSequence3(p[0], p[1] != 0, p[2], p[3], p[4], p[5]);
+ stack.push(ScriptVar(idx));
break;
+ }
- default:
+ case 4:
+ // SequenceList_setAnimRange
+ EXTRACT_PARAMS(3);
+ _madsVm->scene()->_sequenceList.setAnimRange(p[0], p[1], p[2]);
+ break;
+
+ case 5:
+ // SequenceList_addSubEntry
+ EXTRACT_PARAMS(4);
+ stack.push(ScriptVar(_madsVm->scene()->_sequenceList.addSubEntry(p[0], (SequenceSubEntryMode)p[1].get(), p[2], p[3])));
+ break;
+
+ case 7: {
+ // quotes_get_pointer
+ EXTRACT_PARAMS(1);
+ const char *quoteStr = _madsVm->globals()->getQuote(p[0]);
+ stack.push(ScriptVar(quoteStr));
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;
- }
+ case 8: {
+ // KernelMessageList_add
+ EXTRACT_PARAMS(8);
+ int msgIndex = _madsVm->scene()->_kernelMessages.add(Common::Point(p[0], p[1]), p[2],
+ p[3], p[4], p[5] | (p[6] << 16), p[7].getStr());
+ stack.push(ScriptVar(msgIndex));
+ break;
+ }
- 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;
- }
+ case 9:
+ // SequenceList_unk3
+ EXTRACT_PARAMS(1);
+ // TODO: Implement unk3 method
+// stack.push(ScriptVar(_madsVm->scene()->_sequenceList.unk3(p[0])));
+ break;
- 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;
- }
+ case 10:
+ // start_sound
+ EXTRACT_PARAMS(1);
+ _madsVm->_sound->playSound(p[0]);
+ break;
- 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;
- }
+ case 11:
+ // SceneLogic_formAnimName
+ EXTRACT_PARAMS(2);
+ stack.push(ScriptVar(formAnimName((char)p[0], p[1])));
+ break;
+
+ case 12:
+ // SpriteList_addSprites
+ EXTRACT_PARAMS(2);
+ stack.push(ScriptVar(_madsVm->scene()->_spriteSlots.addSprites(p[0].getStr(), false, p[1])));
+ break;
+
+ case 13:
+ // hotspot_activate
+ EXTRACT_PARAMS(2);
+ // TODO: Implement setActive version that takes in a hotspot Id
+// _madsVm->scene()->getSceneResources().hotspots->setActive(p[0], p[1] != 0);
+ break;
- 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;
+ case 14: {
+ // DynamicHotspots_add
+ EXTRACT_PARAMS(7);
+ int idx = _madsVm->scene()->_dynamicHotspots.add(p[0], p[1], p[2],
+ Common::Rect(p[6], p[5], p[6] + p[4], p[5] + p[3]));
+ stack.push(ScriptVar(idx));
+ break;
+ }
+
+ case 15:
+ // SequenceList_setDepth
+ EXTRACT_PARAMS(2);
+ _madsVm->scene()->_sequenceList.setDepth(p[0], p[1]);
+ break;
+
+ case 16: {
+ // quotes_load
+ // Quotes loading can take an arbitrary number of quote Ids, terminated by a 0
+ int firstId = -1;
+ int quoteId;
+ while ((quoteId = stack.pop()) != 0) {
+ if (firstId == -1)
+ firstId = quoteId;
+ _madsVm->globals()->loadQuote(quoteId);
}
- }
+
+ if (firstId != -1)
+ stack.push(ScriptVar(_madsVm->globals()->getQuote(firstId)));
+ break;
+ }
+
+ case 17: {
+ // form_resource_name
+ EXTRACT_PARAMS(4);
+ const char *suffix = NULL;
+ if (p[4].isInt()) {
+ // If integer provided for suffix, it must be a value of 0 (NULL)
+ uint32 vTemp = p[4] | stack.pop();
+ assert(!vTemp);
+ } else
+ suffix = p[4].getStr();
+
+ stack.push(ScriptVar(MADSResourceManager::getResourceName((char)p[1], p[0], (ExtensionType)p[3].get(),
+ suffix, (int16)p[2])));
+ break;
+ }
+
+ case 18:
+ // MadsScene_loadAnimation
+ EXTRACT_PARAMS(3);
+ _madsVm->scene()->loadAnimation(p[1].getStr(), p[0]);
+ break;
+
+ case 19: {
+ // Action_isAction
+ int verbId = stack.pop();
+ int objectNameId = (verbId == 0) ? 0 : stack.pop().get();
+ int indirectObjectId = (objectNameId == 0) ? 0 : stack.pop().get();
+
+ stack.push(ScriptVar(_madsVm->scene()->_action.isAction(verbId, objectNameId, indirectObjectId)));
+ break;
+ }
+
+ case 21:
+ // DynamicHotspots_remove
+ EXTRACT_PARAMS(1);
+ _madsVm->scene()->_dynamicHotspots.remove(p[0]);
+ break;
+
+ case 22: {
+ // object_is_present
+ EXTRACT_PARAMS(1);
+ const MadsObject *obj = _madsVm->globals()->getObject(p[0]);
+ stack.push(ScriptVar((obj->roomNumber == _madsVm->scene()->_currentScene)));
+ break;
+ }
+
+ case 23:
+ // inventory_add
+ EXTRACT_PARAMS(1);
+ _madsVm->scene()->getInterface()->addObjectToInventory(p[0]);
+ break;
+
+ case 24: {
+ // dialog_picture_show
+ EXTRACT_PARAMS(3);
+ int messageId = p[0] | (p[1] << 16);
+ int objectNum = p[2];
+ warning("TODO: Implement dialog with picture. MessageId=%d, objectNum=%d", messageId, objectNum);
+ break;
+ }
+
+ case 25: {
+ // object_is_in_inventory
+ EXTRACT_PARAMS(1);
+ const MadsObject *obj = _madsVm->globals()->getObject(p[0]);
+ stack.push(ScriptVar(obj->isInInventory()));
+ break;
+ }
+
+ default:
+ error("Unknown subroutine %d called", subIndex);
+ break;
+ }
}
+#undef EXTRACT_PARAMS
+
}
diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h
index ec6eff368b..adafe6f93d 100644
--- a/engines/m4/mads_logic.h
+++ b/engines/m4/mads_logic.h
@@ -29,10 +29,38 @@
#ifndef M4_MADS_LOGIC_H
#define M4_MADS_LOGIC_H
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/stack.h"
#include "m4/mads_views.h"
namespace M4 {
+union ScriptVarValue {
+ const char *strValue;
+ uint32 intValue;
+};
+
+/**
+ * Specifies a script variable that either be a 32-bit unsigned integer or a string pointer
+ */
+class ScriptVar {
+private:
+ ScriptVarValue _value;
+ bool _isInt;
+public:
+ ScriptVar(uint32 v = 0) { _value.intValue = v; _isInt = true; }
+ ScriptVar(const char *s) { _value.strValue = s; _isInt = false; }
+
+ void set(uint32 v) { _value.intValue = v; _isInt = true; }
+ void set(const char *s) { _value.strValue = s; _isInt = false; }
+ const char *getStr() const { assert(!_isInt); return _value.strValue; }
+ uint32 get() const { assert(_isInt); return _value.intValue; }
+ bool isInt() const { return _isInt; }
+
+ operator int() { return get(); }
+};
+
class MadsSceneLogic {
private:
// Library interface methods
@@ -41,27 +69,45 @@ private:
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];
+ byte *_scriptsData;
+ int _scriptsSize;
+ Common::HashMap<Common::String, uint32> _subroutines;
+ Common::Array<uint32> _subroutineOffsets;
+
+ enum SubFormatIndex {SUBFORMAT_ENTER, SUBFORMAT_STEP, SUBFORMAT_PREACTIONS, SUBFORMAT_ACTIONS};
+ static const char *subFormatList[];
+ static const char *_opcodeStrings[];
// Support functions
const char *formAnimName(char sepChar, int16 suffixNum);
void getSceneSpriteSet();
void getAnimName();
- IntStorage &dataMap();
+ DataMap &dataMap();
+ void getCallParameters(int numParams, Common::Stack<ScriptVar> &stack, ScriptVar *callParams);
public:
+ MadsSceneLogic() { _scriptsData = NULL; }
+ ~MadsSceneLogic() { delete _scriptsData; }
+
+ void initialiseScripts();
+ void initialiseDataMap();
void selectScene(int sceneNum);
void setupScene();
- void enterScene();
+ void doEnterScene();
void doPreactions();
void doAction();
void doSceneStep();
+
+ void execute(const Common::String &scriptName);
+ void execute(uint32 scriptOffset);
+ uint32 getParam(uint32 &scriptOffset, int opcode);
+ void callSubroutine(int subIndex, Common::Stack<ScriptVar> &stack);
};
class MadsGameLogic {
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index 810acb04fb..7e2e6f500b 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -668,8 +668,10 @@ void RexDialogView::loadBackground() {
break;
case 8:
screenId = 924;
+ break;
case 9:
screenId = 920;
+ break;
default:
error("Unknown scene number");
}
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index d44fa2a753..641ee756e3 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -62,12 +62,14 @@ void SceneNode::load(Common::SeekableReadStream *stream) {
MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) {
_vm = vm;
_activeAnimation = NULL;
+ _animActive = false;
MadsView::_bgSurface = Scene::_backgroundSurface;
MadsView::_depthSurface = Scene::_walkSurface;
_interfaceSurface = new MadsInterfaceView(vm);
_showMousePos = false;
_mouseMsgIndex = -1;
+ _previousScene = -1;
}
MadsScene::~MadsScene() {
@@ -174,7 +176,7 @@ void MadsScene::loadScene(int sceneNumber) {
// Do any scene specific setup
if (_vm->getGameType() == GType_RexNebular)
- _sceneLogic.enterScene();
+ _sceneLogic.doEnterScene();
// Miscellaneous player setup
_madsVm->_player._destPos = _madsVm->_player._destPos;
@@ -215,6 +217,7 @@ void MadsScene::leaveScene() {
if (_activeAnimation) {
delete _activeAnimation;
_activeAnimation = NULL;
+ _animActive = false;
}
Scene::leaveScene();
@@ -383,6 +386,7 @@ void MadsScene::updateState() {
if (((MadsAnimation *) _activeAnimation)->freeFlag() || freeFlag) {
delete _activeAnimation;
_activeAnimation = NULL;
+ _animActive = false;
}
}
@@ -454,6 +458,7 @@ void MadsScene::freeAnimation() {
delete _activeAnimation;
_activeAnimation = NULL;
+ _animActive = false;
}
@@ -573,6 +578,7 @@ void MadsScene::loadAnimation(const Common::String &animName, int abortTimers) {
MadsAnimation *anim = new MadsAnimation(_vm, this);
anim->load(animName.c_str(), abortTimers);
_activeAnimation = anim;
+ _animActive = true;
}
bool MadsScene::getDepthHighBit(const Common::Point &pt) {
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index 7723058cc7..a029c63a4b 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -108,9 +108,14 @@ public:
Common::Point _destPos;
int _destFacing;
Common::Point _customDest;
+ bool _animActive;
public:
MadsScene(MadsEngine *vm);
virtual ~MadsScene();
+ void initialise() {
+ _sceneLogic.initialiseScripts();
+ _sceneLogic.initialiseDataMap();
+ }
// Methods that differ between engines
virtual void loadScene(int sceneNumber);
diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp
index 3e5f0c2ac9..cc127032eb 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -394,14 +394,21 @@ int MadsSpriteSlots::addSprites(const char *resName, bool suppressErrors, int fl
return -1;
}
+ // Append on a '.SS' suffix if the resource doesn't already have an extension
+ char buffer[100];
+ strncpy(buffer, resName, 95);
+ buffer[95] = '\0';
+ if (!strchr(buffer, '.'))
+ strcat(buffer, ".SS");
+
// Get the sprite set
- Common::SeekableReadStream *data = _vm->res()->get(resName);
- SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName, false, flags);
+ Common::SeekableReadStream *data = _vm->res()->get(buffer);
+ SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), buffer, false, flags);
spriteSet->translate(_madsVm->_palette);
assert(spriteSet != NULL);
_sprites.push_back(spriteSet);
- _vm->res()->toss(resName);
+ _vm->res()->toss(buffer);
return _sprites.size() - 1;
}
diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp
index 8457f2087a..57d63c153a 100644
--- a/engines/m4/scene.cpp
+++ b/engines/m4/scene.cpp
@@ -51,6 +51,7 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0,
_interfacePal = NULL;
_interfaceSurface = NULL;
_vm->_rails->setCodeSurface(_walkSurface);
+ _currentScene = -1;
}
Scene::~Scene() {
diff --git a/engines/m4/scene.h b/engines/m4/scene.h
index 9262a7c828..e0b28e6454 100644
--- a/engines/m4/scene.h
+++ b/engines/m4/scene.h
@@ -77,9 +77,6 @@ class Scene : public View {
private:
HotSpotList _sceneHotspots;
protected:
- int _currentScene;
- int _previousScene;
- int _nextScene;
GameInterfaceView *_interfaceSurface;
M4Surface *_backgroundSurface;
M4Surface *_walkSurface;
@@ -87,6 +84,10 @@ protected:
RGBList *_interfacePal;
SceneResources *_sceneResources;
public:
+ int _currentScene;
+ int _previousScene;
+ int _nextScene;
+public:
Scene(MadsM4Engine *vm, SceneResources *res);
virtual ~Scene();
diff --git a/engines/m4/ws_machine.cpp b/engines/m4/ws_machine.cpp
index 985ceedb2e..cb791026c9 100644
--- a/engines/m4/ws_machine.cpp
+++ b/engines/m4/ws_machine.cpp
@@ -157,12 +157,12 @@ int32 Machine::execInstruction() {
_code->loadInstruction(instruction);
if (instruction.instr >= 64) {
- if (machineConditionalsTable[instruction.instr - 64] != NULL)
+ if (machineConditionalsTable[instruction.instr - 64] != 0)
(this->*machineConditionalsTable[instruction.instr - 64])(instruction);
/* The next line is to yield on unimplemented opcodes */
else { fflush(stdout); g_system->delayMillis(5000); }
} else if (instruction.instr > 0) {
- if (machineCommandsTable[instruction.instr] != NULL)
+ if (machineCommandsTable[instruction.instr] != 0)
done = !(this->*machineCommandsTable[instruction.instr])(instruction);
/* The next line is to yield on unimplemented opcodes */
else { fflush(stdout); g_system->delayMillis(5000); }
diff --git a/engines/m4/ws_sequence.cpp b/engines/m4/ws_sequence.cpp
index 1f3c977609..79869a81d0 100644
--- a/engines/m4/ws_sequence.cpp
+++ b/engines/m4/ws_sequence.cpp
@@ -226,7 +226,7 @@ bool Sequence::runProgram() {
while (!done) {
Instruction instruction;
_code->loadInstruction(instruction);
- if (sequenceCommandsTable[instruction.instr] != NULL)
+ if (sequenceCommandsTable[instruction.instr] != 0)
done = !(this->*sequenceCommandsTable[instruction.instr])(instruction);
else { fflush(stdout); /*g_system->delayMillis(1000);*/ }
}
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index 20b4dc1e1b..4b59723772 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -106,6 +106,7 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_music = new MusicPlayer(driver);
_music->setNativeMT32(native_mt32);
+ _music->open();
//_music->setAdLib(adlib);
// Set default sound frequency
diff --git a/engines/made/music.cpp b/engines/made/music.cpp
index bb45367805..4e2789e5fa 100644
--- a/engines/made/music.cpp
+++ b/engines/made/music.cpp
@@ -40,7 +40,6 @@ namespace Made {
MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
- this->open();
_xmidiParser = MidiParser::createParser_XMIDI();
_smfParser = MidiParser::createParser_SMF();
}
@@ -81,6 +80,11 @@ int MusicPlayer::open() {
if (ret)
return ret;
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
_driver->setTimerCallback(this, &onTimer);
return 0;
}
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 5dcfff4f90..3eed08e3c1 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -309,6 +309,7 @@ RivenConsole::RivenConsole(MohawkEngine_Riven *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("listZipCards", WRAP_METHOD(RivenConsole, Cmd_ListZipCards));
DCmd_Register("getRMAP", WRAP_METHOD(RivenConsole, Cmd_GetRMAP));
DCmd_Register("combos", WRAP_METHOD(RivenConsole, Cmd_Combos));
+ DCmd_Register("sliderState", WRAP_METHOD(RivenConsole, Cmd_SliderState));
}
RivenConsole::~RivenConsole() {
@@ -349,7 +350,7 @@ bool RivenConsole::Cmd_Var(int argc, const char **argv) {
return true;
}
- uint32 *globalVar = _vm->matchVarToString(argv[1]);
+ uint32 *globalVar = _vm->getVar(argv[1]);
if (!globalVar) {
DebugPrintf("Unknown variable \'%s\'\n", argv[1]);
@@ -366,19 +367,13 @@ bool RivenConsole::Cmd_Var(int argc, const char **argv) {
bool RivenConsole::Cmd_PlaySound(int argc, const char **argv) {
if (argc < 2) {
- DebugPrintf("Usage: playSound <value> (<use main sound file, default = true>)\n");
- DebugPrintf("The main sound file is default, but you can use the word \'false\' to make it use the current stack file.\n");
-
+ DebugPrintf("Usage: playSound <value>\n");
return true;
}
_vm->_sound->stopSound();
_vm->_sound->stopAllSLST();
-
- bool mainSoundFile = (argc < 3) || (scumm_stricmp(argv[2], "false") != 0);
-
- _vm->_sound->playSound((uint16)atoi(argv[1]), mainSoundFile);
-
+ _vm->_sound->playSound((uint16)atoi(argv[1]));
return false;
}
@@ -392,13 +387,9 @@ bool RivenConsole::Cmd_PlaySLST(int argc, const char **argv) {
_vm->_sound->stopSound();
_vm->_sound->stopAllSLST();
- uint16 card = _vm->getCurCard();
-
- if (argc == 3)
- card = (uint16)atoi(argv[2]);
+ uint16 card = (argc == 3) ? (uint16)atoi(argv[2]) : _vm->getCurCard();
_vm->_sound->playSLST((uint16)atoi(argv[1]), card);
-
return false;
}
@@ -407,7 +398,6 @@ bool RivenConsole::Cmd_StopSound(int argc, const char **argv) {
_vm->_sound->stopSound();
_vm->_sound->stopAllSLST();
-
return true;
}
@@ -466,10 +456,11 @@ bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
DebugPrintf("Hotspot %d, index %d, BLST ID %d (", i, _vm->_hotspots[i].index, _vm->_hotspots[i].blstID);
if (_vm->_hotspots[i].enabled)
- DebugPrintf("enabled)\n");
+ DebugPrintf("enabled");
else
- DebugPrintf("disabled)\n");
+ DebugPrintf("disabled");
+ DebugPrintf(") - (%d, %d, %d, %d)\n", _vm->_hotspots[i].rect.left, _vm->_hotspots[i].rect.top, _vm->_hotspots[i].rect.right, _vm->_hotspots[i].rect.bottom);
DebugPrintf(" Name = %s\n", _vm->getHotspotName(i).c_str());
}
@@ -477,7 +468,7 @@ bool RivenConsole::Cmd_Hotspots(int argc, const char **argv) {
}
bool RivenConsole::Cmd_ZipMode(int argc, const char **argv) {
- uint32 *zipModeActive = _vm->matchVarToString("azip");
+ uint32 *zipModeActive = _vm->getVar("azip");
*zipModeActive = !(*zipModeActive);
DebugPrintf("Zip Mode is ");
@@ -620,9 +611,9 @@ bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
// 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");
+ uint32 teleCombo = *_vm->getVar("tcorrectorder");
+ uint32 prisonCombo = *_vm->getVar("pcorrectorder");
+ uint32 domeCombo = *_vm->getVar("adomecombo");
DebugPrintf("Telescope Combo:\n ");
for (int i = 0; i < 5; i++)
@@ -641,6 +632,14 @@ bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
return true;
}
+bool RivenConsole::Cmd_SliderState(int argc, const char **argv) {
+ if (argc > 1)
+ _vm->_externalScriptHandler->setDomeSliderState((uint32)atoi(argv[1]));
+
+ DebugPrintf("Dome Slider State = %08x\n", _vm->_externalScriptHandler->getDomeSliderState());
+ 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 1806c61027..beba3f8852 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -89,6 +89,7 @@ private:
bool Cmd_ListZipCards(int argc, const char **argv);
bool Cmd_GetRMAP(int argc, const char **argv);
bool Cmd_Combos(int argc, const char **argv);
+ bool Cmd_SliderState(int argc, const char **argv);
};
class LivingBooksConsole : public GUI::Debugger {
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index c09763cbb3..a7369b4825 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -125,17 +125,17 @@ RivenOptionsDialog::~RivenOptionsDialog() {
void RivenOptionsDialog::open() {
Dialog::open();
- _zipModeCheckbox->setState(*_vm->matchVarToString("azip") != 0);
- _waterEffectCheckbox->setState(*_vm->matchVarToString("waterenabled") != 0);
+ _zipModeCheckbox->setState(*_vm->getVar("azip") != 0);
+ _waterEffectCheckbox->setState(*_vm->getVar("waterenabled") != 0);
}
void RivenOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
switch (cmd) {
case kZipCmd:
- *_vm->matchVarToString("azip") = _zipModeCheckbox->getState() ? 1 : 0;
+ *_vm->getVar("azip") = _zipModeCheckbox->getState() ? 1 : 0;
break;
case kWaterCmd:
- *_vm->matchVarToString("waterenabled") = _waterEffectCheckbox->getState() ? 1 : 0;
+ *_vm->getVar("waterenabled") = _waterEffectCheckbox->getState() ? 1 : 0;
break;
case GUI::kCloseCmd:
close();
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index 1974aec9c2..f472a9d721 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -326,7 +326,7 @@ void RivenGraphics::drawPLST(uint16 x) {
// draw PLST 1 each time. This "hack" is here to catch any PLST attempting to draw
// twice. There should never be a problem with doing it this way.
if (index == x && !(Common::find(_activatedPLSTs.begin(), _activatedPLSTs.end(), x) != _activatedPLSTs.end())) {
- debug (0, "Drawing image %d", id);
+ debug(0, "Drawing image %d", id);
copyImageToScreen(id, left, top, right, bottom);
_activatedPLSTs.push_back(x);
break;
@@ -399,7 +399,7 @@ void RivenGraphics::clearWaterEffects() {
bool RivenGraphics::runScheduledWaterEffects() {
// Don't run the effect if it's disabled
- if (*_vm->matchVarToString("waterenabled") == 0)
+ if (*_vm->getVar("waterenabled") == 0)
return false;
Graphics::Surface *screen = NULL;
@@ -491,95 +491,106 @@ void RivenGraphics::runScheduledTransition() {
_scheduledTransition = -1; // Clear scheduled transition
}
-// TODO: Marble Cursors/Palettes
void RivenGraphics::changeCursor(uint16 num) {
// All of Riven's cursors are hardcoded. See riven_cursors.h for these definitions.
switch (num) {
case 1002:
// Zip Mode
- CursorMan.replaceCursor(zipModeCursor, 16, 16, 8, 8, 0);
- CursorMan.replaceCursorPalette(zipModeCursorPalette, 1, ARRAYSIZE(zipModeCursorPalette) / 4);
+ CursorMan.replaceCursor(s_zipModeCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(s_zipModeCursorPalette, 1, ARRAYSIZE(s_zipModeCursorPalette) / 4);
break;
case 2003:
// Hand Over Object
- CursorMan.replaceCursor(objectHandCursor, 16, 16, 8, 8, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_objectHandCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 2004:
// Grabbing/Using Object
- CursorMan.replaceCursor(grabbingHandCursor, 13, 13, 6, 6, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_grabbingHandCursor, 13, 13, 6, 6, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3000:
// Standard Hand
- CursorMan.replaceCursor(standardHandCursor, 15, 16, 6, 0, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_standardHandCursor, 15, 16, 6, 0, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3001:
// Pointing Left
- CursorMan.replaceCursor(pointingLeftCursor, 15, 13, 0, 3, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingLeftCursor, 15, 13, 0, 3, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3002:
// Pointing Right
- CursorMan.replaceCursor(pointingRightCursor, 15, 13, 14, 3, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingRightCursor, 15, 13, 14, 3, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3003:
// Pointing Down (Palm Up)
- CursorMan.replaceCursor(pointingDownCursorPalmUp, 13, 16, 3, 15, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingDownCursorPalmUp, 13, 16, 3, 15, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3004:
// Pointing Up (Palm Up)
- CursorMan.replaceCursor(pointingUpCursorPalmUp, 13, 16, 3, 0, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingUpCursorPalmUp, 13, 16, 3, 0, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3005:
// Pointing Left (Curved)
- CursorMan.replaceCursor(pointingLeftCursorBent, 15, 13, 0, 5, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingLeftCursorBent, 15, 13, 0, 5, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3006:
// Pointing Right (Curved)
- CursorMan.replaceCursor(pointingRightCursorBent, 15, 13, 14, 5, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingRightCursorBent, 15, 13, 14, 5, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 3007:
// Pointing Down (Palm Down)
- CursorMan.replaceCursor(pointingDownCursorPalmDown, 15, 16, 7, 15, 0);
- CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pointingDownCursorPalmDown, 15, 16, 7, 15, 0);
+ CursorMan.replaceCursorPalette(s_handCursorPalette, 1, ARRAYSIZE(s_handCursorPalette) / 4);
break;
case 4001:
// Red Marble
+ CursorMan.replaceCursor(s_redMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_redMarbleCursorPalette, 1, ARRAYSIZE(s_redMarbleCursorPalette) / 4);
break;
case 4002:
// Orange Marble
+ CursorMan.replaceCursor(s_orangeMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_orangeMarbleCursorPalette, 1, ARRAYSIZE(s_orangeMarbleCursorPalette) / 4);
break;
case 4003:
// Yellow Marble
+ CursorMan.replaceCursor(s_yellowMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_yellowMarbleCursorPalette, 1, ARRAYSIZE(s_yellowMarbleCursorPalette) / 4);
break;
case 4004:
// Green Marble
+ CursorMan.replaceCursor(s_greenMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_greenMarbleCursorPalette, 1, ARRAYSIZE(s_greenMarbleCursorPalette) / 4);
break;
case 4005:
// Blue Marble
+ CursorMan.replaceCursor(s_blueMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_blueMarbleCursorPalette, 1, ARRAYSIZE(s_blueMarbleCursorPalette) / 4);
break;
case 4006:
- // Purple Marble
+ // Violet Marble
+ CursorMan.replaceCursor(s_violetMarbleCursor, 12, 12, 5, 5, 0);
+ CursorMan.replaceCursorPalette(s_violetMarbleCursorPalette, 1, ARRAYSIZE(s_violetMarbleCursorPalette) / 4);
break;
case 5000:
// Pellet
- CursorMan.replaceCursor(pelletCursor, 8, 8, 4, 4, 0);
- CursorMan.replaceCursorPalette(pelletCursorPalette, 1, ARRAYSIZE(pelletCursorPalette) / 4);
+ CursorMan.replaceCursor(s_pelletCursor, 8, 8, 4, 4, 0);
+ CursorMan.replaceCursorPalette(s_pelletCursorPalette, 1, ARRAYSIZE(s_pelletCursorPalette) / 4);
break;
case 9000:
// Hide Cursor
CursorMan.showMouse(false);
break;
default:
- error ("Cursor %d does not exist!", num);
+ error("Cursor %d does not exist!", num);
}
if (num != 9000) // Show Cursor
@@ -597,28 +608,35 @@ void RivenGraphics::showInventory() {
// Clear the inventory area
clearInventoryArea();
- // The demo doesn't have the inventory system and we don't want
- // to show the inventory on setup screens or in other journals.
- if (_vm->getFeatures() & GF_DEMO || _vm->getCurStack() == aspit)
- return;
-
- // 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;
-
- if (!hasCathBook) {
- drawInventoryImage(101, g_atrusJournalRect1);
- } else if (!hasTrapBook) {
- drawInventoryImage(101, g_atrusJournalRect2);
- drawInventoryImage(102, g_cathJournalRect2);
+ // Draw the demo's exit button
+ if (_vm->getFeatures() & GF_DEMO) {
+ // extras.mhk tBMP 101 contains "EXIT" instead of Atrus' journal in the demo!
+ // The demo's extras.mhk contains all the other inventory/marble/credits image
+ // but has hacked tBMP 101 with "EXIT". *sigh*
+ drawInventoryImage(101, g_demoExitRect);
} else {
- drawInventoryImage(101, g_atrusJournalRect3);
- drawInventoryImage(102, g_cathJournalRect3);
- drawInventoryImage(100, g_trapBookRect3);
+ // We don't want to show the inventory on setup screens or in other journals.
+ if (_vm->getCurStack() == aspit)
+ return;
+
+ // 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->getVar("acathbook") != 0;
+ bool hasTrapBook = *_vm->getVar("atrapbook") != 0;
+
+ if (!hasCathBook) {
+ drawInventoryImage(101, g_atrusJournalRect1);
+ } else if (!hasTrapBook) {
+ drawInventoryImage(101, g_atrusJournalRect2);
+ drawInventoryImage(102, g_cathJournalRect2);
+ } else {
+ drawInventoryImage(101, g_atrusJournalRect3);
+ drawInventoryImage(102, g_cathJournalRect3);
+ drawInventoryImage(100, g_trapBookRect3);
+ }
}
_vm->_system->updateScreen();
@@ -672,6 +690,39 @@ void RivenGraphics::drawRect(Common::Rect rect, bool active) {
_vm->_system->unlockScreen();
}
+void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect) {
+ // Draw tBMP id from srcRect to dstRect
+ ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getRawData(ID_TBMP, id));
+ Graphics::Surface *surface = imageData->getSurface();
+ delete imageData;
+
+ assert(srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height());
+
+ for (uint16 i = 0; i < srcRect.height(); i++)
+ memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(srcRect.left, i + srcRect.top), srcRect.width() * surface->bytesPerPixel);
+
+ surface->free();
+ delete surface;
+
+ _dirtyScreen = true;
+}
+
+void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) {
+ ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
+ Graphics::Surface *surface = imageData->getSurface();
+ delete imageData;
+
+ assert(dstRect.width() == surface->w);
+
+ for (uint16 i = 0; i < surface->h; i++)
+ memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(0, i), surface->pitch);
+
+ surface->free();
+ delete surface;
+
+ _dirtyScreen = true;
+}
+
LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm) : _vm(vm) {
_bmpDecoder = (_vm->getGameType() == GType_LIVINGBOOKSV1) ? new OldMohawkBitmap() : new MohawkBitmap();
_palette = new byte[256 * 4];
@@ -707,7 +758,7 @@ void LBGraphics::copyImageToScreen(uint16 image, uint16 left, uint16 right) {
}
void LBGraphics::setPalette(uint16 id) {
- // Old Living Books gamnes use the old CTBL-style palette format while newer
+ // Old Living Books games use the old CTBL-style palette format while newer
// games use the better tPAL format which can store partial palettes.
if (_vm->getGameType() == GType_LIVINGBOOKSV1) {
diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h
index dd1764e6d6..9419aad277 100644
--- a/engines/mohawk/graphics.h
+++ b/engines/mohawk/graphics.h
@@ -41,6 +41,8 @@ class MohawkBitmap;
class MystBitmap;
enum {
+ kRivenOpenHandCursor = 2003,
+ kRivenClosedHandCursor = 2004,
kRivenMainCursor = 3000,
kRivenPelletCursor = 5000,
kRivenHideCursor = 9000
@@ -117,7 +119,7 @@ private:
uint16 type;
uint16 width;
uint16 height;
- } *entries;
+ } *entries;
Common::File picFile;
} _pictureFile;
@@ -147,6 +149,8 @@ public:
Common::Array<uint16> _activatedPLSTs;
void drawPLST(uint16 x);
void drawRect(Common::Rect rect, bool active);
+ void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect);
+ void drawExtrasImage(uint16 id, Common::Rect dstRect);
// Water Effect
void scheduleWaterEffect(uint16);
@@ -181,7 +185,6 @@ private:
Graphics::Surface *_mainScreen;
bool _dirtyScreen;
Graphics::PixelFormat _pixelFormat;
- byte findBlackIndex();
};
class LBGraphics {
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index 9ff301c129..6ed7a313a0 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -25,6 +25,7 @@
#include "common/config-manager.h"
#include "common/debug-channels.h"
+#include "common/translation.h"
#include "mohawk/graphics.h"
#include "mohawk/myst.h"
@@ -218,7 +219,7 @@ Common::Error MohawkEngine_Myst::run() {
_varStore = new MystVar(this);
_saveLoad = new MystSaveLoad(this, _saveFileMan);
_scriptParser = new MystScriptParser(this);
- _loadDialog = new GUI::SaveLoadChooser("Load Game:", "Load");
+ _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"));
_loadDialog->setSaveMode(false);
_optionsDialog = new MystOptionsDialog(this);
@@ -427,7 +428,7 @@ void MohawkEngine_Myst::changeToCard(uint16 card) {
// NOTE: All sounds are looped when played via the sound section of the
// VIEW resources.
- _sound->playSound(soundAction, true, soundActionVolume, true);
+ _sound->playSound(soundAction, soundActionVolume, true);
} else {
error("Unknown sound action %d", soundAction);
}
diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp
index 2f6d178da8..a8cd643e2c 100644
--- a/engines/mohawk/myst_scripts.cpp
+++ b/engines/mohawk/myst_scripts.cpp
@@ -709,10 +709,7 @@ void MystScriptParser::playSoundBlocking(uint16 op, uint16 var, uint16 argc, uin
debugC(kDebugScript, "Opcode %d: playSoundBlocking", op);
debugC(kDebugScript, "\tsoundId: %d", soundId);
- Audio::SoundHandle *handle = _vm->_sound->playSound(soundId);
-
- while (_vm->_mixer->isSoundHandleActive(*handle))
- _vm->_system->delayMillis(10);
+ _vm->_sound->playSoundBlocking(soundId);
} else
unknown(op, var, argc, argv);
}
@@ -870,7 +867,7 @@ void MystScriptParser::opcode_30(uint16 op, uint16 var, uint16 argc, uint16 *arg
_vm->_sound->stopSound();
// TODO: Need to keep sound handle and add function to change volume of
// looped running sound for kMystSoundActionChangeVolume type
- _vm->_sound->playSound(soundAction, true, soundVolume);
+ _vm->_sound->playSound(soundAction, soundVolume);
} else {
debugC(kDebugScript, "Unknown");
warning("Unknown sound control value in opcode %d", op);
@@ -2458,7 +2455,7 @@ void MystScriptParser::opcode_120(uint16 op, uint16 var, uint16 argc, uint16 *ar
if (var8 != 0xFFFF)
_vm->_varStore->setVar(var8, !_vm->_varStore->getVar(var8));
else
- warning("Opcode 120: No invoking Resource Var 8 found!");
+ warning("Opcode 120: No invoking Resource Var 8 found");
} else
unknown(op, var, argc, argv);
break;
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index 07b08dc220..f2f4a42f0e 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -27,6 +27,7 @@
#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/keyboard.h"
+#include "common/translation.h"
#include "mohawk/graphics.h"
#include "mohawk/resource.h"
@@ -44,6 +45,7 @@ Common::Rect *g_cathJournalRect2;
Common::Rect *g_atrusJournalRect3;
Common::Rect *g_cathJournalRect3;
Common::Rect *g_trapBookRect3;
+Common::Rect *g_demoExitRect;
MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
_showHotspots = false;
@@ -73,6 +75,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
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);
+ g_demoExitRect = new Common::Rect(291, 408, 317, 419);
}
MohawkEngine_Riven::~MohawkEngine_Riven() {
@@ -92,13 +95,13 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
delete g_atrusJournalRect3;
delete g_cathJournalRect3;
delete g_trapBookRect3;
+ delete g_demoExitRect;
}
GUI::Debugger *MohawkEngine_Riven::getDebugger() {
return _console;
}
-
Common::Error MohawkEngine_Riven::run() {
MohawkEngine::run();
@@ -114,124 +117,133 @@ Common::Error MohawkEngine_Riven::run() {
initVars();
- // Open extras.mhk for common images (non-existant in the demo)
- if (!(getFeatures() & GF_DEMO)) {
- _extrasFile = new MohawkArchive();
- _extrasFile->open("extras.mhk");
- }
+ // Open extras.mhk for common images
+ _extrasFile = new MohawkArchive();
+ _extrasFile->open("extras.mhk");
// Start at main cursor
_gfx->changeCursor(kRivenMainCursor);
- // Load game from launcher/command line if requested
- if (ConfMan.hasKey("save_slot") && !(getFeatures() & GF_DEMO)) {
+ // Let's begin, shall we?
+ if (getFeatures() & GF_DEMO) {
+ // Start the demo off with the videos
+ changeToStack(aspit);
+ changeToCard(6);
+ } else if (ConfMan.hasKey("save_slot")) {
+ // Load game from launcher/command line if requested
uint32 gameToLoad = ConfMan.getInt("save_slot");
Common::StringArray savedGamesList = _saveLoad->generateSaveGameList();
if (gameToLoad > savedGamesList.size())
error ("Could not find saved game");
_saveLoad->loadGame(savedGamesList[gameToLoad]);
- } else { // Otherwise, start us off at aspit's card 1
+ } else {
+ // Otherwise, start us off at aspit's card 1 (the main menu)
changeToStack(aspit);
changeToCard(1);
}
+
+ while (!_gameOver && !shouldQuit())
+ handleEvents();
+
+ return Common::kNoError;
+}
+
+void MohawkEngine_Riven::handleEvents() {
Common::Event event;
- while (!_gameOver) {
- bool needsUpdate = _gfx->runScheduledWaterEffects();
- needsUpdate |= _video->updateBackgroundMovies();
- while (_eventMan->pollEvent(event)) {
- switch (event.type) {
- case Common::EVENT_MOUSEMOVE:
- _mousePos = event.mouse;
- checkHotspotChange();
+ // Update background videos and the water effect
+ bool needsUpdate = _gfx->runScheduledWaterEffects();
+ needsUpdate |= _video->updateBackgroundMovies();
+
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ checkHotspotChange();
- // Check to show the inventory
- if (_mousePos.y >= 392)
+ if (!(getFeatures() & GF_DEMO)) {
+ // Check to show the inventory, but it is always "showing" in the demo
+ if (_eventMan->getMousePos().y >= 392)
_gfx->showInventory();
else
_gfx->hideInventory();
+ }
- needsUpdate = true;
- break;
- case Common::EVENT_LBUTTONDOWN:
+ needsUpdate = true;
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ if (_curHotspot >= 0)
+ runHotspotScript(_curHotspot, kMouseDownScript);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ // See RivenScript::switchCard() for more information on why we sometimes
+ // disable the next up event.
+ if (!_ignoreNextMouseUp) {
if (_curHotspot >= 0)
- runHotspotScript(_curHotspot, kMouseDownScript);
+ runHotspotScript(_curHotspot, kMouseUpScript);
+ else
+ checkInventoryClick();
+ }
+ _ignoreNextMouseUp = false;
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_d:
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ _console->attach();
+ _console->onFrame();
+ }
break;
- case Common::EVENT_LBUTTONUP:
- // 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();
+ case Common::KEYCODE_SPACE:
+ pauseGame();
+ break;
+ case Common::KEYCODE_F4:
+ _showHotspots = !_showHotspots;
+ if (_showHotspots) {
+ for (uint16 i = 0; i < _hotspotCount; i++)
+ _gfx->drawRect(_hotspots[i].rect, _hotspots[i].enabled);
+ needsUpdate = true;
+ } else
+ refreshCard();
+ break;
+ case Common::KEYCODE_F5:
+ runDialog(*_optionsDialog);
+ updateZipMode();
+ break;
+ case Common::KEYCODE_r:
+ // Return to the main menu in the demo on ctrl+r
+ if (event.kbd.flags & Common::KBD_CTRL && getFeatures() & GF_DEMO) {
+ if (_curStack != aspit)
+ changeToStack(aspit);
+ changeToCard(1);
}
- _ignoreNextMouseUp = false;
break;
- case Common::EVENT_KEYDOWN:
- switch (event.kbd.keycode) {
- case Common::KEYCODE_d:
- if (event.kbd.flags & Common::KBD_CTRL) {
- _console->attach();
- _console->onFrame();
- }
- break;
- case Common::KEYCODE_SPACE:
- pauseGame();
- break;
- case Common::KEYCODE_F4:
- _showHotspots = !_showHotspots;
- if (_showHotspots) {
- for (uint16 i = 0; i < _hotspotCount; i++)
- _gfx->drawRect(_hotspots[i].rect, _hotspots[i].enabled);
- needsUpdate = true;
- } else
- refreshCard();
- break;
- case Common::KEYCODE_F5:
- runDialog(*_optionsDialog);
- updateZipMode();
- break;
- case Common::KEYCODE_ESCAPE:
- if (getFeatures() & GF_DEMO) {
- if (_curStack != aspit)
- changeToStack(aspit);
- changeToCard(1);
- }
- break;
- default:
- break;
+ case Common::KEYCODE_p:
+ // Play the intro videos in the demo on ctrl+p
+ if (event.kbd.flags & Common::KBD_CTRL && getFeatures() & GF_DEMO) {
+ if (_curStack != aspit)
+ changeToStack(aspit);
+ changeToCard(6);
}
break;
default:
break;
}
+ break;
+ default:
+ break;
}
+ }
- if (_curHotspot >= 0)
- runHotspotScript(_curHotspot, kMouseInsideScript);
-
- if (shouldQuit()) {
- if (_eventMan->shouldRTL() && (getFeatures() & GF_DEMO) && !(_curStack == aspit && _curCard == 12)) {
- if (_curStack != aspit)
- changeToStack(aspit);
- changeToCard(12);
- _eventMan->resetRTL();
- continue;
- }
- return Common::kNoError;
- }
-
- // Update the screen if we need to
- if (needsUpdate)
- _system->updateScreen();
+ if (_curHotspot >= 0)
+ runHotspotScript(_curHotspot, kMouseInsideScript);
- // Cut down on CPU usage
- _system->delayMillis(10);
- }
+ // Update the screen if we need to
+ if (needsUpdate)
+ _system->updateScreen();
- return Common::kNoError;
+ // Cut down on CPU usage
+ _system->delayMillis(10);
}
// Stack/Card-Related Functions
@@ -240,7 +252,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
// The endings are in reverse order because of the way the 1.02 patch works.
// The only "Data3" file is j_Data3.mhk from that patch. Patch files have higher
// priorities over the regular files and are therefore loaded and checked first.
- static const char *endings[] = { "_Data3.mhk", "_Data2.mhk", "_Data1.mhk", "_Data.mhk" };
+ static const char *endings[] = { "_Data3.mhk", "_Data2.mhk", "_Data1.mhk", "_Data.mhk", "_Sounds.mhk" };
// Don't change stack to the current stack (if the files are loaded)
if (_curStack == n && !_mhk.empty())
@@ -274,9 +286,8 @@ void MohawkEngine_Riven::changeToStack(uint16 n) {
if (_mhk.empty())
error("Could not load stack %s", getStackName(_curStack).c_str());
- // Stop any currently playing sounds and load the new sound file too
+ // Stop any currently playing sounds
_sound->stopAllSLST();
- _sound->loadRivenSounds(_curStack);
}
// Riven uses some hacks to change stacks for linking books
@@ -379,12 +390,11 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
// NOTE: The hotspot scripts are cleared by the RivenScriptManager automatically.
- Common::SeekableReadStream* inStream = getRawData(ID_HSPT, id);
+ Common::SeekableReadStream *inStream = getRawData(ID_HSPT, id);
_hotspotCount = inStream->readUint16BE();
_hotspots = new RivenHotspot[_hotspotCount];
-
for (uint16 i = 0; i < _hotspotCount; i++) {
_hotspots[i].enabled = true;
@@ -396,26 +406,21 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
int16 right = inStream->readSint16BE();
int16 bottom = inStream->readSint16BE();
- // Riven has some weird hotspots, disable them here
+ // Riven has some invalid rects, disable them here
+ // Known weird hotspots:
+ // - tspit 371 (DVD: 377), hotspot 4
if (left >= right || top >= bottom) {
- left = top = right = bottom = 0;
- _hotspots[i].enabled = 0;
+ warning("%s %d hotspot %d is invalid: (%d, %d, %d, %d)", getStackName(_curStack).c_str(), _curCard, i, left, top, right, bottom);
+ left = top = right = bottom = 0;
+ _hotspots[i].enabled = 0;
}
_hotspots[i].rect = Common::Rect(left, top, right, bottom);
_hotspots[i].u0 = inStream->readUint16BE();
-
- if (_hotspots[i].u0 != 0)
- warning("Hotspot %d u0 non-zero", i);
-
_hotspots[i].mouse_cursor = inStream->readUint16BE();
_hotspots[i].index = inStream->readUint16BE();
_hotspots[i].u1 = inStream->readSint16BE();
-
- if (_hotspots[i].u1 != -1)
- warning("Hotspot %d u1 not -1", i);
-
_hotspots[i].zipModeHotspot = inStream->readUint16BE();
// Read in the scripts now
@@ -431,7 +436,7 @@ void MohawkEngine_Riven::updateZipMode() {
for (uint32 i = 0; i < _hotspotCount; i++) {
if (_hotspots[i].zipModeHotspot) {
- if (*matchVarToString("azip") != 0) {
+ if (*getVar("azip") != 0) {
// Check if a zip mode hotspot is enabled by checking the name/id against the ZIPS records.
Common::String hotspotName = getName(HotspotNames, _hotspots[i].name_resource);
@@ -455,7 +460,7 @@ void MohawkEngine_Riven::checkHotspotChange() {
uint16 hotspotIndex = 0;
bool foundHotspot = false;
for (uint16 i = 0; i < _hotspotCount; i++)
- if (_hotspots[i].enabled && _hotspots[i].rect.contains(_mousePos)) {
+ if (_hotspots[i].enabled && _hotspots[i].rect.contains(_eventMan->getMousePos())) {
foundHotspot = true;
hotspotIndex = i;
}
@@ -481,50 +486,71 @@ Common::String MohawkEngine_Riven::getHotspotName(uint16 hotspot) {
}
void MohawkEngine_Riven::checkInventoryClick() {
+ Common::Point mousePos = _eventMan->getMousePos();
+
// Don't even bother. We're not in the inventory portion of the screen.
- if (_mousePos.y < 392)
+ if (mousePos.y < 392)
+ return;
+
+ // In the demo, check if we've clicked the exit button
+ if (getFeatures() & GF_DEMO) {
+ if (g_demoExitRect->contains(mousePos)) {
+ if (_curStack == aspit && _curCard == 1) {
+ // From the main menu, go to the "quit" screen
+ changeToCard(12);
+ } else if (_curStack == aspit && _curCard == 12) {
+ // From the "quit" screen, just quit
+ _gameOver = true;
+ } else {
+ // Otherwise, return to the main menu
+ if (_curStack != aspit)
+ changeToStack(aspit);
+ changeToCard(1);
+ }
+ }
return;
+ }
- // No inventory in the demo or opening screens.
- if (getFeatures() & GF_DEMO || _curStack == aspit)
+ // No inventory shown on aspit
+ if (_curStack == aspit)
return;
// Set the return stack/card id's.
- *matchVarToString("returnstackid") = _curStack;
- *matchVarToString("returncardid") = _curCard;
+ *getVar("returnstackid") = _curStack;
+ *getVar("returncardid") = _curCard;
// See RivenGraphics::showInventory() for an explanation
// of the variables' meanings.
- bool hasCathBook = *matchVarToString("acathbook") != 0;
- bool hasTrapBook = *matchVarToString("atrapbook") != 0;
+ bool hasCathBook = *getVar("acathbook") != 0;
+ bool hasTrapBook = *getVar("atrapbook") != 0;
// Go to the book if a hotspot contains the mouse
if (!hasCathBook) {
- if (g_atrusJournalRect1->contains(_mousePos)) {
+ if (g_atrusJournalRect1->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
}
} else if (!hasTrapBook) {
- if (g_atrusJournalRect2->contains(_mousePos)) {
+ if (g_atrusJournalRect2->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
- } else if (g_cathJournalRect2->contains(_mousePos)) {
+ } else if (g_cathJournalRect2->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(6);
}
} else {
- if (g_atrusJournalRect3->contains(_mousePos)) {
+ if (g_atrusJournalRect3->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
- } else if (g_cathJournalRect3->contains(_mousePos)) {
+ } else if (g_cathJournalRect3->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(6);
- } else if (g_trapBookRect3->contains(_mousePos)) {
+ } else if (g_trapBookRect3->contains(mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(7);
@@ -607,8 +633,26 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
}
}
+void MohawkEngine_Riven::delayAndUpdate(uint32 ms) {
+ uint32 startTime = _system->getMillis();
+
+ while (_system->getMillis() < startTime + ms && !shouldQuit()) {
+ bool needsUpdate = _gfx->runScheduledWaterEffects();
+ needsUpdate |= _video->updateBackgroundMovies();
+
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event))
+ ;
+
+ if (needsUpdate)
+ _system->updateScreen();
+
+ _system->delayMillis(10); // Ease off the CPU
+ }
+}
+
void MohawkEngine_Riven::runLoadDialog() {
- GUI::SaveLoadChooser slc("Load Game:", "Load");
+ GUI::SaveLoadChooser slc(_("Load game:"), _("Load"));
slc.setSaveMode(false);
Common::String gameId = ConfMan.get("gameid");
@@ -636,19 +680,19 @@ Common::Error MohawkEngine_Riven::saveGameState(int slot, const char *desc) {
return _saveLoad->saveGame(Common::String(desc)) ? Common::kNoError : Common::kUnknownError;
}
-static const char *rivenStackNames[] = {
- "aspit",
- "bspit",
- "gspit",
- "jspit",
- "ospit",
- "pspit",
- "rspit",
- "tspit"
-};
-
-Common::String MohawkEngine_Riven::getStackName(uint16 stack) {
- return Common::String(rivenStackNames[stack]);
+Common::String MohawkEngine_Riven::getStackName(uint16 stack) const {
+ static const char *rivenStackNames[] = {
+ "aspit",
+ "bspit",
+ "gspit",
+ "jspit",
+ "ospit",
+ "pspit",
+ "rspit",
+ "tspit"
+ };
+
+ return rivenStackNames[stack];
}
bool ZipMode::operator== (const ZipMode &z) const {
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 631285455e..251a0fc753 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -74,6 +74,7 @@ extern Common::Rect *g_cathJournalRect2;
extern Common::Rect *g_atrusJournalRect3;
extern Common::Rect *g_cathJournalRect3;
extern Common::Rect *g_trapBookRect3;
+extern Common::Rect *g_demoExitRect;
struct RivenHotspot {
uint16 blstID;
@@ -135,11 +136,11 @@ private:
uint16 _curCard;
uint16 _curStack;
void loadCard(uint16);
+ void handleEvents();
// Hotspot related functions and variables
uint16 _hotspotCount;
void loadHotspots(uint16);
- void checkHotspotChange();
void checkInventoryClick();
bool _showHotspots;
void updateZipMode();
@@ -153,41 +154,44 @@ private:
bool _ignoreNextMouseUp;
public:
- Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
- bool _activatedSLST;
- void runLoadDialog();
-
+ // Stack/card/script funtions
void changeToCard(uint16 dest);
void changeToStack(uint16);
void refreshCard();
Common::String getName(uint16 nameResource, uint16 nameID);
- Common::String getStackName(uint16 stack);
+ Common::String getStackName(uint16 stack) const;
void runCardScript(uint16 scriptType);
void runUpdateScreenScript() { runCardScript(kCardUpdateScript); }
- uint16 getCurCard() { return _curCard; }
- uint16 getCurStack() { return _curStack; }
+ uint16 getCurCard() const { return _curCard; }
+ uint16 getCurStack() const { return _curStack; }
uint16 matchRMAPToCard(uint32);
uint32 getCurCardRMAP();
- Common::Point _mousePos;
+ // Hotspot functions/variables
RivenHotspot *_hotspots;
int32 _curHotspot;
Common::Array<ZipMode> _zipModeData;
- uint16 getHotspotCount() { return _hotspotCount; }
+ uint16 getHotspotCount() const { return _hotspotCount; }
void runHotspotScript(uint16 hotspot, uint16 scriptType);
- int32 getCurHotspot() { return _curHotspot; }
+ int32 getCurHotspot() const { return _curHotspot; }
Common::String getHotspotName(uint16 hotspot);
+ void checkHotspotChange();
+ // Variable functions
void initVars();
- uint32 getVarCount() { return _varCount; }
+ uint32 getVarCount() const { return _varCount; }
uint32 getGlobalVar(uint32 index);
Common::String getGlobalVarName(uint32 index);
uint32 *getLocalVar(uint32 index);
- uint32 *matchVarToString(Common::String varName);
- uint32 *matchVarToString(const char *varName);
+ uint32 *getVar(const Common::String &varName);
+ // Miscellaneous
void setGameOver() { _gameOver = true; }
void ignoreNextMouseUp() { _ignoreNextMouseUp = true; }
+ Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id);
+ bool _activatedSLST;
+ void runLoadDialog();
+ void delayAndUpdate(uint32 ms);
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_cursors.h b/engines/mohawk/riven_cursors.h
index 7deaf9c33c..71cb8fd804 100644
--- a/engines/mohawk/riven_cursors.h
+++ b/engines/mohawk/riven_cursors.h
@@ -37,7 +37,7 @@ namespace Mohawk {
// 1 = Black (0x000000)
// 2 = Yellow (0xDCFF00)
////////////////////////////////////////
-static const byte zipModeCursor[] = {
+static const byte s_zipModeCursor[] = {
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -61,7 +61,7 @@ static const byte zipModeCursor[] = {
// Zip Mode Cursor Palette:
// Palette For The Zip Mode Cursor
////////////////////////////////////////
-static const byte zipModeCursorPalette[] = {
+static const byte s_zipModeCursorPalette[] = {
0x00, 0x00, 0x00, 0x00, // Black
0xDC, 0xFF, 0x00, 0x00, // Yellow
};
@@ -77,7 +77,7 @@ static const byte zipModeCursorPalette[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte objectHandCursor[] = {
+static const byte s_objectHandCursor[] = {
0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 0, 1, 2, 3, 1, 1, 1, 0, 0, 0, 0,
0, 0, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 0, 0, 0,
@@ -107,7 +107,7 @@ static const byte objectHandCursor[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte grabbingHandCursor[] = {
+static const byte s_grabbingHandCursor[] = {
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 2, 3, 1, 1, 1, 0, 0, 0,
0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 0,
@@ -134,7 +134,7 @@ static const byte grabbingHandCursor[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte standardHandCursor[] = {
+static const byte s_standardHandCursor[] = {
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0,
@@ -164,7 +164,7 @@ static const byte standardHandCursor[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingLeftCursor[] = {
+static const byte s_pointingLeftCursor[] = {
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 3, 2, 2, 2, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 3, 1, 1,
@@ -191,7 +191,7 @@ static const byte pointingLeftCursor[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingRightCursor[] = {
+static const byte s_pointingRightCursor[] = {
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 2, 2, 2, 3, 1, 0, 0, 0, 0, 0, 0,
1, 1, 3, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0,
@@ -218,7 +218,7 @@ static const byte pointingRightCursor[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingDownCursorPalmUp[] = {
+static const byte s_pointingDownCursorPalmUp[] = {
0, 0, 1, 4, 2, 2, 2, 2, 2, 4, 1, 0, 0,
0, 0, 1, 4, 2, 2, 4, 2, 2, 2, 4, 1, 0,
0, 1, 3, 4, 2, 2, 4, 4, 4, 4, 4, 1, 0,
@@ -248,7 +248,7 @@ static const byte pointingDownCursorPalmUp[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingUpCursorPalmUp[] = {
+static const byte s_pointingUpCursorPalmUp[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 4, 4, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 1, 0, 0,
@@ -278,7 +278,7 @@ static const byte pointingUpCursorPalmUp[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingLeftCursorBent[] = {
+static const byte s_pointingLeftCursorBent[] = {
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 3, 2, 2, 2, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 3, 1, 1,
@@ -305,7 +305,7 @@ static const byte pointingLeftCursorBent[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingRightCursorBent[] = {
+static const byte s_pointingRightCursorBent[] = {
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 1, 2, 2, 2, 3, 1, 0, 0, 0, 0, 0, 0,
1, 1, 3, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0,
@@ -332,7 +332,7 @@ static const byte pointingRightCursorBent[] = {
// 3 = Brown (0x8A672F)
// 4 = Dark Peach (0xE89A62)
////////////////////////////////////////
-static const byte pointingDownCursorPalmDown[] = {
+static const byte s_pointingDownCursorPalmDown[] = {
0, 1, 3, 4, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0,
0, 1, 3, 4, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0,
0, 1, 3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
@@ -355,7 +355,7 @@ static const byte pointingDownCursorPalmDown[] = {
// Hand Cursor Palette:
// Palette For All Hand Cursors
////////////////////////////////////////
-static const byte handCursorPalette[] = {
+static const byte s_handCursorPalette[] = {
0x00, 0x00, 0x00, 0x00, // Black
0xED, 0xCD, 0x96, 0x00, // Light Peach
0x8A, 0x67, 0x2F, 0x00, // Brown
@@ -376,7 +376,7 @@ static const byte handCursorPalette[] = {
// 6 = Dark Green (0x2D3300)
// 7 = Darkest Gray (0x222222)
////////////////////////////////////////
-static const byte pelletCursor[] = {
+static const byte s_pelletCursor[] = {
0, 0, 1, 1, 2, 3, 0, 0,
0, 2, 1, 4, 1, 2, 5, 0,
4, 1, 4, 1, 2, 1, 5, 4,
@@ -391,7 +391,7 @@ static const byte pelletCursor[] = {
// Pellet Cursor Palette:
// Palette For The Pellet Cursor
////////////////////////////////////////
-static const byte pelletCursorPalette[] = {
+static const byte s_pelletCursorPalette[] = {
0x5D, 0x67, 0x30, 0x00,
0x5E, 0x33, 0x33, 0x00,
0x55, 0x55, 0x55, 0x00,
@@ -401,4 +401,270 @@ static const byte pelletCursorPalette[] = {
0x22, 0x22, 0x22, 0x00
};
+////////////////////////////////////////
+// Red Marble Cursor (12x12):
+// Cursor When Holding The Red Marble
+////////////////////////////////////////
+static const byte s_redMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 2, 2, 2, 0, 0, 0, 0,
+ 0, 0, 3, 4, 2, 5, 2, 2, 2, 2, 0, 0,
+ 0, 6, 1, 1, 2, 2, 5, 2, 2, 2, 2, 0,
+ 0, 6, 3, 4, 5, 2, 2, 7, 8, 5, 2, 0,
+ 9, 6, 10,11,2, 2, 2, 12,13,2, 2, 2,
+ 14,10,6, 4, 1, 2, 8, 2, 2, 5, 2, 2,
+ 15,16,6, 3, 1, 2, 2, 2, 2, 2, 2, 5,
+ 17,9,18, 3, 4, 4, 4, 5, 2, 5, 1, 2,
+ 0, 16,9, 6, 6, 19,1, 20,1, 4, 11,0,
+ 0, 17,15,18,9, 10,6, 10,3, 21,4, 0,
+ 0, 0, 18,15,9, 18,6, 22,10,23,0, 0,
+ 0, 0, 0, 0, 15,15,16,9, 0, 0, 0, 0
+};
+
+
+////////////////////////////////////////
+// Red Marble Cursor Palette:
+// Palette For The Red Marble Cursor
+////////////////////////////////////////
+static const byte s_redMarbleCursorPalette[] = {
+ 0xb8, 0x33, 0x32, 0x00,
+ 0xe5, 0x33, 0x31, 0x00,
+ 0x98, 0x06, 0x00, 0x00,
+ 0xb8, 0x00, 0x34, 0x00,
+ 0xe6, 0x00, 0x34, 0x00,
+ 0x7a, 0x04, 0x00, 0x00,
+ 0xe8, 0x9a, 0x62, 0x00,
+ 0xea, 0x31, 0x67, 0x00,
+ 0x6a, 0x03, 0x00, 0x00,
+ 0x8c, 0x00, 0x35, 0x00,
+ 0xb6, 0x36, 0x00, 0x00,
+ 0xed, 0xcd, 0x96, 0x00,
+ 0xe9, 0x66, 0x65, 0x00,
+ 0x5b, 0x35, 0x00, 0x00,
+ 0x5b, 0x02, 0x00, 0x00,
+ 0x5f, 0x00, 0x35, 0x00,
+ 0x4c, 0x01, 0x00, 0x00,
+ 0x5e, 0x33, 0x33, 0x00,
+ 0x89, 0x05, 0x00, 0x00,
+ 0xb6, 0x08, 0x00, 0x00,
+ 0xa7, 0x07, 0x00, 0x00,
+ 0x88, 0x36, 0x00, 0x00,
+ 0x8b, 0x33, 0x33, 0x00
+};
+
+////////////////////////////////////////
+// Orange Marble Cursor (12x12):
+// Cursor When Holding The Orange Marble
+////////////////////////////////////////
+static const byte s_orangeMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 2, 2, 3, 0, 0, 0, 0,
+ 0, 0, 4, 5, 2, 2, 3, 3, 3, 3, 0, 0,
+ 0, 6, 7, 4, 2, 1, 2, 2, 3, 3, 3, 0,
+ 0, 6, 6, 7, 1, 2, 3, 8, 9, 2, 10,0,
+ 11,12,7, 4, 2, 3, 3, 13,9, 2, 2, 1,
+ 14,15,6, 4, 2, 16,3, 3, 2, 1, 1, 1,
+ 14,14,12,17,4, 2, 2, 1, 2, 1, 2, 1,
+ 14,18,12,6, 4, 4, 4, 19,2, 19,20,4,
+ 0, 14,14,15,6, 15,6, 4, 4, 4, 4, 0,
+ 0, 14,11,14,14,12,12,12,17,6, 17,0,
+ 0, 0, 14,14,17,14,17,6, 6, 17,0, 0,
+ 0, 0, 0, 0, 14,11,14,11,0, 0, 0, 0
+};
+
+////////////////////////////////////////
+// Orange Marble Cursor Palette:
+// Palette For The Orange Marble Cursor
+////////////////////////////////////////
+static const byte s_orangeMarbleCursorPalette[] = {
+ 0xe1, 0x9e, 0x00, 0x00,
+ 0xe3, 0x9b, 0x28, 0x00,
+ 0xe2, 0xcf, 0x20, 0x00,
+ 0xb5, 0x6a, 0x00, 0x00,
+ 0xb6, 0x9b, 0x29, 0x00,
+ 0x87, 0x69, 0x00, 0x00,
+ 0xb7, 0x67, 0x2f, 0x00,
+ 0xe9, 0xff, 0x93, 0x00,
+ 0xe1, 0xff, 0x5a, 0x00,
+ 0xe0, 0xd0, 0x00, 0x00,
+ 0x5e, 0x33, 0x33, 0x00,
+ 0x88, 0x36, 0x00, 0x00,
+ 0xf3, 0xff, 0xc9, 0x00,
+ 0x5b, 0x35, 0x00, 0x00,
+ 0x8b, 0x33, 0x33, 0x00,
+ 0xe6, 0xce, 0x5f, 0x00,
+ 0x8a, 0x67, 0x2f, 0x00,
+ 0x5d, 0x67, 0x30, 0x00,
+ 0xe2, 0x6a, 0x00, 0x00,
+ 0xb3, 0x9d, 0x00, 0x00
+};
+
+////////////////////////////////////////
+// Yellow Marble Cursor (12x12):
+// Cursor When Holding The Yellow Marble
+////////////////////////////////////////
+static const byte s_yellowMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0,
+ 0, 0, 3, 4, 1, 1, 1, 5, 6, 6, 0, 0,
+ 0, 3, 3, 7, 1, 1, 1, 1, 2, 1, 6, 0,
+ 0, 3, 3, 3, 3, 1, 1, 8, 6, 1, 6, 0,
+ 9, 9, 3, 3, 1, 1, 2, 10,8, 1, 1, 2,
+ 11,9, 3, 3, 1, 1, 1, 1, 1, 1, 5, 1,
+ 9, 9, 12,3, 3, 1, 1, 1, 1, 1, 1, 1,
+ 9, 9, 9, 3, 3, 3, 3, 3, 1, 1, 1, 1,
+ 0, 11,9, 9, 12,3, 3, 3, 3, 3, 3, 0,
+ 0, 9, 9, 13,9, 14,12,3, 3, 3, 3, 0,
+ 0, 0, 9, 9, 9, 12,14,3, 13,3, 0, 0,
+ 0, 0, 0, 0, 11,9, 11,9, 0, 0, 0, 0
+};
+
+////////////////////////////////////////
+// Yellow Marble Cursor Palette:
+// Palette For The Yellow Marble Cursor
+////////////////////////////////////////
+static const byte s_yellowMarbleCursorPalette[] = {
+ 0xb3, 0xd0, 0x00, 0x00,
+ 0xb0, 0xff, 0x00, 0x00,
+ 0x86, 0x9c, 0x00, 0x00,
+ 0x87, 0xd0, 0x00, 0x00,
+ 0xe0, 0xd0, 0x00, 0x00,
+ 0xdc, 0xff, 0x00, 0x00,
+ 0xb3, 0x9d, 0x00, 0x00,
+ 0xdc, 0xff, 0x11, 0x00,
+ 0x5a, 0x68, 0x00, 0x00,
+ 0xe1, 0xff, 0x5a, 0x00,
+ 0x5d, 0x67, 0x30, 0x00,
+ 0x87, 0x69, 0x00, 0x00,
+ 0x88, 0x9b, 0x2a, 0x00,
+ 0x5a, 0x9c, 0x00, 0x00
+};
+
+////////////////////////////////////////
+// Green Marble Cursor (12x12):
+// Cursor When Holding The Green Marble
+////////////////////////////////////////
+static const byte s_greenMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 2, 3, 3, 0, 0, 0, 0,
+ 0, 0, 4, 5, 2, 1, 2, 3, 6, 6, 0, 0,
+ 0, 7, 5, 8, 8, 1, 1, 2, 3, 6, 6, 0,
+ 0, 7, 7, 4, 8, 1, 2, 9, 6, 2, 6, 0,
+ 10,7, 7, 4, 1, 2, 3, 11,12,2, 2, 3,
+ 13,13,7, 4, 1, 2, 3, 2, 1, 2, 2, 3,
+ 14,13,7, 7, 5, 1, 1, 8, 2, 1, 1, 2,
+ 15,16,13,7, 4, 4, 5, 5, 1, 8, 1, 1,
+ 0, 15,13,7, 7, 7, 4, 4, 4, 5, 8, 0,
+ 0, 14,16,15,13, 7, 7, 7, 4,17,5, 0,
+ 0, 0, 10,16,13,13,13,17,18,17,0, 0,
+ 0, 0, 0, 0, 15,10,19,10,0, 0, 0, 0
+};
+
+////////////////////////////////////////
+// Green Marble Cursor Palette:
+// Palette For The Green Marble Cursor
+////////////////////////////////////////
+static const byte s_greenMarbleCursorPalette[] = {
+ 0x0e, 0xd0, 0x00, 0x00,
+ 0x0f, 0xe1, 0x00, 0x00,
+ 0x10, 0xf2, 0x00, 0x00,
+ 0x0b, 0x9c, 0x00, 0x00,
+ 0x0c, 0xad, 0x00, 0x00,
+ 0x11, 0xff, 0x00, 0x00,
+ 0x09, 0x8a, 0x00, 0x00,
+ 0x0d, 0xbe, 0x00, 0x00,
+ 0x30, 0xff, 0x5a, 0x00,
+ 0x0d, 0x67, 0x30, 0x00,
+ 0x6b, 0xff, 0x92, 0x00,
+ 0x00, 0xff, 0x28, 0x00,
+ 0x08, 0x79, 0x00, 0x00,
+ 0x05, 0x57, 0x00, 0x00,
+ 0x30, 0x67, 0x30, 0x00,
+ 0x06, 0x68, 0x00, 0x00,
+ 0x00, 0x9b, 0x2c, 0x00,
+ 0x2e, 0x9c, 0x00, 0x00,
+ 0x2e, 0x68, 0x00, 0x00
+};
+
+////////////////////////////////////////
+// Blue Marble Cursor (12x12):
+// Cursor When Holding The Blue Marble
+////////////////////////////////////////
+static const byte s_blueMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 2, 3, 3, 0, 0, 0, 0,
+ 0, 0, 4, 5, 2, 2, 6, 3, 7, 3, 0, 0,
+ 0, 8, 9, 5, 10,11,2, 6, 3, 3, 7, 0,
+ 0, 12,13,9, 10,11,6, 14,7, 6, 3, 0,
+ 15,8, 4, 13,2, 6, 3, 16,17,6, 6, 3,
+ 18,15,19,13,10,7, 3, 6, 2, 2, 6, 7,
+ 20,8, 18,4, 21,11,2, 10,6, 2, 2, 2,
+ 15,15,18,8, 13,9, 21,5, 11,10,2, 1,
+ 0, 8, 15,19,15,13,13,21,21,5, 9, 0,
+ 0, 22,20,15, 8,19,15,19,4, 9, 4, 0,
+ 0, 0, 15,20,15,15,19,15,9, 15,0, 0,
+ 0, 0, 0, 0, 20,15, 8,15,0, 0, 0, 0
+};
+
+////////////////////////////////////////
+// Blue Marble Cursor Palette:
+// Palette For The Blue Marble Cursor
+////////////////////////////////////////
+static const byte s_blueMarbleCursorPalette[] = {
+ 0x6b, 0x00, 0xd2, 0x00,
+ 0x66, 0x00, 0xe3, 0x00,
+ 0x72, 0x00, 0xff, 0x00,
+ 0x53, 0x2d, 0x9d, 0x00,
+ 0x4e, 0x00, 0xaf, 0x00,
+ 0x6d, 0x00, 0xf5, 0x00,
+ 0x7d, 0x00, 0xff, 0x00,
+ 0x44, 0x00, 0x69, 0x00,
+ 0x56, 0x00, 0x9d, 0x00,
+ 0x56, 0x00, 0xc0, 0x00,
+ 0x5e, 0x00, 0xd2, 0x00,
+ 0x2b, 0x31, 0x68, 0x00,
+ 0x3f, 0x00, 0x8c, 0x00,
+ 0x91, 0x22, 0xff, 0x00,
+ 0x41, 0x31, 0x68, 0x00,
+ 0xd7, 0x95, 0xff, 0x00,
+ 0x77, 0x22, 0xff, 0x00,
+ 0x2f, 0x00, 0x69, 0x00,
+ 0x37, 0x00, 0x7a, 0x00,
+ 0x27, 0x00, 0x58, 0x00,
+ 0x46, 0x00, 0x9d, 0x00,
+ 0x33, 0x33, 0x33, 0x00
+};
+
+////////////////////////////////////////
+// Violet Marble Cursor (12x12):
+// Cursor When Holding The Violet Marble
+////////////////////////////////////////
+static const byte s_violetMarbleCursor[] = {
+ 0, 0, 0, 0, 1, 1, 1, 2, 0, 0, 0, 0,
+ 0, 0, 3, 3, 1, 1, 1, 4, 2, 4, 0, 0,
+ 0, 3, 3, 3, 1, 5, 1, 1, 4, 2, 4, 0,
+ 0, 3, 3, 3, 3, 1, 1, 6, 4, 1, 2, 0,
+ 3, 7, 8, 3, 1, 1, 4, 9, 4, 1, 1, 4,
+ 8, 7, 8, 3, 10,4, 1, 1, 1, 1, 4, 1,
+ 8, 3, 8, 7, 3, 1, 1, 5, 1, 1, 1, 1,
+ 7, 7, 11,3, 3, 3, 3, 3, 1, 3, 1, 1,
+ 0, 8, 7, 7, 8, 8, 7, 3, 3, 3, 1, 0,
+ 0, 7, 8, 3, 11,7, 3, 11,3, 10,3, 0,
+ 0, 0, 8, 7, 3, 3, 7, 3, 3, 3, 0, 0,
+ 0, 0, 0, 0, 8, 7, 11,3, 0, 0, 0, 0
+};
+
+////////////////////////////////////////
+// Violet Marble Cursor Palette:
+// Palette For The Violet Marble Cursor
+////////////////////////////////////////
+static const byte s_violetMarbleCursorPalette[] = {
+ 0xaa, 0x00, 0xd1, 0x00,
+ 0xd8, 0x00, 0xff, 0x00,
+ 0x76, 0x00, 0x9d, 0x00,
+ 0xb5, 0x00, 0xff, 0x00,
+ 0x87, 0x00, 0xd2, 0x00,
+ 0xd7, 0x22, 0xff, 0x00,
+ 0x68, 0x00, 0x69, 0x00,
+ 0x44, 0x00, 0x69, 0x00,
+ 0xd7, 0x5e, 0xff, 0x00,
+ 0x9c, 0x00, 0x9d, 0x00,
+ 0x56, 0x00, 0x9d, 0x00
+};
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 67d621a54c..21464a6a48 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -34,8 +34,12 @@
namespace Mohawk {
+static const uint32 kDomeSliderDefaultState = 0x01F00000;
+static const uint32 kDomeSliderSlotCount = 25;
+
RivenExternal::RivenExternal(MohawkEngine_Riven *vm) : _vm(vm) {
setupCommands();
+ _sliderState = kDomeSliderDefaultState;
}
RivenExternal::~RivenExternal() {
@@ -64,6 +68,8 @@ void RivenExternal::setupCommands() {
COMMAND(xadisablemenureturn);
COMMAND(xaenablemenureturn);
COMMAND(xalaunchbrowser);
+ COMMAND(xadisablemenuintro);
+ COMMAND(xaenablemenuintro);
// bspit (Bookmaking Island) external commands
COMMAND(xblabopenbook);
@@ -200,16 +206,33 @@ void RivenExternal::runCommand(uint16 argc, uint16 *argv) {
}
void RivenExternal::runDemoBoundaryDialog() {
- GUI::MessageDialog dialog("This demo does not allow you\n"
- "to visit that part of Riven.");
+ GUI::MessageDialog dialog("Exploration beyond this point available only within the full version of\n"
+ "the game.");
dialog.runModal();
}
void RivenExternal::runEndGame(uint16 video) {
_vm->_sound->stopAllSLST();
- _vm->_video->playMovieBlocking(video);
+ _vm->_video->playMovie(video);
+ runCredits(video);
+}
+void RivenExternal::runCredits(uint16 video) {
// TODO: Play until the last frame and then run the credits
+
+ VideoHandle videoHandle = _vm->_video->findVideoHandle(video);
+
+ while (!_vm->_video->endOfVideo(videoHandle) && !_vm->shouldQuit()) {
+ if (_vm->_video->updateBackgroundMovies())
+ _vm->_system->updateScreen();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event))
+ ;
+
+ _vm->_system->delayMillis(10);
+ }
+
_vm->setGameOver();
}
@@ -231,7 +254,151 @@ void RivenExternal::runDomeCheck() {
// 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;
+ *_vm->getVar("domecheck") = 1;
+}
+
+void RivenExternal::resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 startHotspot) {
+ // The rightmost slider should move left until it finds the next slider,
+ // then those two continue until they find the third slider. This continues
+ // until all five sliders have returned their starting slots.
+ byte slidersFound = 0;
+ for (uint32 i = 0; i < kDomeSliderSlotCount; i++) {
+ if (_sliderState & (1 << i)) {
+ // A slider occupies this spot. Increase the number of sliders we
+ // have found, but we're not doing any moving this iteration.
+ slidersFound++;
+ } else {
+ // Move all the sliders we have found over one slot
+ for (byte j = 0; j < slidersFound; j++) {
+ _sliderState &= ~(1 << (i - j - 1));
+ _sliderState |= 1 << (i - j);
+ }
+
+ // If we have at least one found slider, it has now moved
+ // so we should redraw and play a tick sound
+ if (slidersFound) {
+ _vm->_sound->playSound(soundId);
+ drawDomeSliders(bitmapId, startHotspot);
+ _vm->_system->delayMillis(10);
+ }
+ }
+ }
+
+ // Sanity checks - the slider count should always be 5 and we should end up at
+ // the default state after moving them all over.
+ assert(slidersFound == 5);
+ assert(_sliderState == kDomeSliderDefaultState);
+}
+
+void RivenExternal::checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDomeHotspot) {
+ // Let's see if we're all matched up...
+ if (*_vm->getVar("adomecombo") == _sliderState) {
+ // Set the button hotspot to the open dome hotspot
+ _vm->_hotspots[resetSlidersHotspot].enabled = false;
+ _vm->_hotspots[openDomeHotspot].enabled = true;
+ } else {
+ // Set the button hotspot to the reset sliders hotspot
+ _vm->_hotspots[resetSlidersHotspot].enabled = true;
+ _vm->_hotspots[openDomeHotspot].enabled = false;
+ }
+}
+
+void RivenExternal::checkSliderCursorChange(uint16 startHotspot) {
+ // Set the cursor based on _sliderState and what hotspot we're over
+ for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+ if (_vm->_hotspots[i + startHotspot].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+ if (_sliderState & (1 << (24 - i)))
+ _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ else
+ _vm->_gfx->changeCursor(kRivenMainCursor);
+ break;
+ }
+ }
+}
+
+void RivenExternal::dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot) {
+ int16 foundSlider = -1;
+
+ for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+ if (_vm->_hotspots[i + startHotspot].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+ // If the slider is not at this hotspot, we can't do anything else
+ if (!(_sliderState & (1 << (24 - i))))
+ return;
+
+ foundSlider = i;
+ break;
+ }
+ }
+
+ // We're not over any slider
+ if (foundSlider < 0)
+ return;
+
+ // We've clicked down, so show the closed hand cursor
+ _vm->_gfx->changeCursor(kRivenClosedHandCursor);
+
+ bool done = false;
+ while (!done) {
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ if (foundSlider < 24 && !(_sliderState & (1 << (23 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot + 1].rect.contains(event.mouse)) {
+ // We've moved the slider right one space
+ _sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+ foundSlider++;
+ _sliderState |= 1 << (24 - foundSlider);
+
+ // Now play a click sound and redraw
+ _vm->_sound->playSound(soundId);
+ drawDomeSliders(bitmapId, startHotspot);
+ } else if (foundSlider > 0 && !(_sliderState & (1 << (25 - foundSlider))) && _vm->_hotspots[foundSlider + startHotspot - 1].rect.contains(event.mouse)) {
+ // We've moved the slider left one space
+ _sliderState &= ~(_sliderState & (1 << (24 - foundSlider)));
+ foundSlider--;
+ _sliderState |= 1 << (24 - foundSlider);
+
+ // Now play a click sound and redraw
+ _vm->_sound->playSound(soundId);
+ drawDomeSliders(bitmapId, startHotspot);
+ } else
+ _vm->_system->updateScreen(); // A normal update for the cursor
+ break;
+ case Common::EVENT_LBUTTONUP:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+ _vm->_system->delayMillis(10);
+ }
+
+ // Check to see if we have the right combination
+ checkDomeSliders(resetSlidersHotspot, openDomeHotspot);
+}
+
+void RivenExternal::drawDomeSliders(uint16 bitmapId, uint16 startHotspot) {
+ Common::Rect dstAreaRect = Common::Rect(200, 250, 420, 319);
+
+ // On pspit, the rect is different by two pixels
+ // (alternatively, we could just use hotspot 3 here, but only on pspit is there a hotspot for this)
+ if (_vm->getCurStack() == pspit)
+ dstAreaRect.translate(-2, 0);
+
+ for (uint16 i = 0; i < kDomeSliderSlotCount; i++) {
+ Common::Rect srcRect = _vm->_hotspots[startHotspot + i].rect;
+ srcRect.translate(-dstAreaRect.left, -dstAreaRect.top); // Adjust the rect so it's in the destination area
+
+ Common::Rect dstRect = _vm->_hotspots[startHotspot + i].rect;
+
+ if (_sliderState & (1 << (24 - i)))
+ _vm->_gfx->drawImageRect(bitmapId, srcRect, dstRect);
+ else
+ _vm->_gfx->drawImageRect(bitmapId + 1, srcRect, dstRect);
+ }
+
+ _vm->_gfx->updateScreen();
}
// ------------------------------------------------------------------------------------
@@ -252,7 +419,7 @@ void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) {
void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
// Get the variable
- uint32 page = *_vm->matchVarToString("aatruspage");
+ uint32 page = *_vm->getVar("aatruspage");
// Set hotspots depending on the page
if (page == 1) {
@@ -271,13 +438,13 @@ void RivenExternal::xaatrusopenbook(uint16 argc, uint16 *argv) {
void RivenExternal::xaatrusbookback(uint16 argc, uint16 *argv) {
// Return to where we were before entering the book
- _vm->changeToStack(*_vm->matchVarToString("returnstackid"));
- _vm->changeToCard(*_vm->matchVarToString("returncardid"));
+ _vm->changeToStack(*_vm->getVar("returnstackid"));
+ _vm->changeToCard(*_vm->getVar("returncardid"));
}
void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("aatruspage");
+ uint32 *page = _vm->getVar("aatruspage");
// Decrement the page if it's not the first page
if (*page == 1)
@@ -286,9 +453,9 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
// Play the page turning sound
if (_vm->getFeatures() & GF_DEMO)
- _vm->_sound->playSound(4, false);
+ _vm->_sound->playSound(4);
else
- _vm->_sound->playSound(3, false);
+ _vm->_sound->playSound(3);
// Now update the screen :)
_vm->_gfx->scheduleTransition(1);
@@ -297,7 +464,7 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("aatruspage");
+ uint32 *page = _vm->getVar("aatruspage");
// Increment the page if it's not the last page
if (((_vm->getFeatures() & GF_DEMO) && *page == 6) || *page == 10)
@@ -306,9 +473,9 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
// Play the page turning sound
if (_vm->getFeatures() & GF_DEMO)
- _vm->_sound->playSound(5, false);
+ _vm->_sound->playSound(5);
else
- _vm->_sound->playSound(4, false);
+ _vm->_sound->playSound(4);
// Now update the screen :)
_vm->_gfx->scheduleTransition(0);
@@ -317,7 +484,7 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
// Get the variable
- uint32 page = *_vm->matchVarToString("acathpage");
+ uint32 page = *_vm->getVar("acathpage");
// Set hotspots depending on the page
if (page == 1) {
@@ -340,19 +507,33 @@ void RivenExternal::xacathopenbook(uint16 argc, uint16 *argv) {
_vm->_gfx->drawPLST(51);
if (page == 28) {
- // TODO: Draw telescope combination
+ // Draw the telescope combination
+ // The images for the numbers are tBMP's 13 through 17.
+ // The start point is at (156, 247)
+ uint32 teleCombo = *_vm->getVar("tcorrectorder");
+ static const uint16 kNumberWidth = 32;
+ static const uint16 kNumberHeight = 25;
+ static const uint16 kDstX = 156;
+ static const uint16 kDstY = 247;
+
+ for (byte i = 0; i < 5; i++) {
+ uint16 offset = (getComboDigit(teleCombo, i) - 1) * kNumberWidth;
+ Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
+ Common::Rect dstRect = Common::Rect(i * kNumberWidth + kDstX, kDstY, (i + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
+ _vm->_gfx->drawImageRect(i + 13, srcRect, dstRect);
+ }
}
}
void RivenExternal::xacathbookback(uint16 argc, uint16 *argv) {
// Return to where we were before entering the book
- _vm->changeToStack(*_vm->matchVarToString("returnstackid"));
- _vm->changeToCard(*_vm->matchVarToString("returncardid"));
+ _vm->changeToStack(*_vm->getVar("returnstackid"));
+ _vm->changeToCard(*_vm->getVar("returncardid"));
}
void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
// Get the variable
- uint32 *page = _vm->matchVarToString("acathpage");
+ uint32 *page = _vm->getVar("acathpage");
// Increment the page if it's not the first page
if (*page == 1)
@@ -360,7 +541,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
(*page)--;
// Play the page turning sound
- _vm->_sound->playSound(5, false);
+ _vm->_sound->playSound(5);
// Now update the screen :)
_vm->_gfx->scheduleTransition(3);
@@ -369,7 +550,7 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
// Get the variable
- uint32 *page = _vm->matchVarToString("acathpage");
+ uint32 *page = _vm->getVar("acathpage");
// Increment the page if it's not the last page
if (*page == 49)
@@ -377,7 +558,7 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
(*page)++;
// Play the page turning sound
- _vm->_sound->playSound(6, false);
+ _vm->_sound->playSound(6);
// Now update the screen :)
_vm->_gfx->scheduleTransition(2);
@@ -386,27 +567,27 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
// Return to where we were before entering the book
- *_vm->matchVarToString("atrap") = 0;
- _vm->changeToStack(*_vm->matchVarToString("returnstackid"));
- _vm->changeToCard(*_vm->matchVarToString("returncardid"));
+ *_vm->getVar("atrap") = 0;
+ _vm->changeToStack(*_vm->getVar("returnstackid"));
+ _vm->changeToCard(*_vm->getVar("returncardid"));
}
void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {
// Close the trap book
- *_vm->matchVarToString("atrap") = 0;
+ *_vm->getVar("atrap") = 0;
// Play the page turning sound
- _vm->_sound->playSound(8, false);
+ _vm->_sound->playSound(8);
_vm->refreshCard();
}
void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {
// Open the trap book
- *_vm->matchVarToString("atrap") = 1;
+ *_vm->getVar("atrap") = 1;
// Play the page turning sound
- _vm->_sound->playSound(9, false);
+ _vm->_sound->playSound(9);
_vm->refreshCard();
}
@@ -417,42 +598,99 @@ void RivenExternal::xarestoregame(uint16 argc, uint16 *argv) {
}
void RivenExternal::xadisablemenureturn(uint16 argc, uint16 *argv) {
- // Dummy function -- implemented in Mohawk::go
+ // This function would normally enable the Windows menu item for
+ // returning to the main menu. Ctrl+r will do this instead.
+ // The original also had this shortcut.
}
void RivenExternal::xaenablemenureturn(uint16 argc, uint16 *argv) {
- // Dummy function -- implemented in Mohawk::go
+ // This function would normally enable the Windows menu item for
+ // returning to the main menu. Ctrl+r will do this instead.
+ // The original also had this shortcut.
}
void RivenExternal::xalaunchbrowser(uint16 argc, uint16 *argv) {
// Well, we can't launch a browser for obvious reasons ;)
+ // The original text is as follows (for reference):
+
+ // If you have an auto-dial configured connection to the Internet,
+ // please select YES below.
+ //
+ // America Online and CompuServe users may experience difficulty. If
+ // you find that you are unable to connect, please quit the Riven
+ // Demo, launch your browser and type in the following URL:
+ //
+ // www.redorb.com/buyriven
+ //
+ // Would you like to attempt to make the connection?
+ //
+ // [YES] [NO]
+
GUI::MessageDialog dialog("At this point, the Riven Demo would\n"
- "open up a web browser to bring you to\n"
- "the Riven website. ScummVM cannot do\n"
- "that. Visit the site on your own.");
+ "ask if you would like to open a web browser\n"
+ "to bring you to the Red Orb store to buy\n"
+ "the game. ScummVM cannot do that and\n"
+ "the site no longer exists.");
dialog.runModal();
}
+void RivenExternal::xadisablemenuintro(uint16 argc, uint16 *argv) {
+ // This function would normally enable the Windows menu item for
+ // playing the intro. Ctrl+p will play the intro movies instead.
+ // The original also had this shortcut.
+
+ // Hide the "exit" button here
+ _vm->_gfx->hideInventory();
+}
+
+void RivenExternal::xaenablemenuintro(uint16 argc, uint16 *argv) {
+ // This function would normally enable the Windows menu item for
+ // playing the intro. Ctrl+p will play the intro movies instead.
+ // The original also had this shortcut.
+
+ // Show the "exit" button here
+ _vm->_gfx->showInventory();
+}
+
// ------------------------------------------------------------------------------------
// bspit (Bookmaking Island) external commands
// ------------------------------------------------------------------------------------
void RivenExternal::xblabopenbook(uint16 argc, uint16 *argv) {
// Get the variable
- uint32 page = *_vm->matchVarToString("blabbook");
+ uint32 page = *_vm->getVar("blabbook");
// Draw the image of the page based on the blabbook variable
_vm->_gfx->drawPLST(page);
- // TODO: Draw the dome combo
if (page == 14) {
- warning ("Need to draw dome combo");
+ // Draw the dome combination
+ // The images for the numbers are tBMP's 364 through 368
+ // The start point is at (240, 82)
+ uint32 domeCombo = *_vm->getVar("adomecombo");
+ static const uint16 kNumberWidth = 32;
+ static const uint16 kNumberHeight = 24;
+ static const uint16 kDstX = 240;
+ static const uint16 kDstY = 82;
+ byte numCount = 0;
+
+ for (int bitPos = 24; bitPos >= 0; bitPos--) {
+ if (domeCombo & (1 << bitPos)) {
+ uint16 offset = (24 - bitPos) * kNumberWidth;
+ Common::Rect srcRect = Common::Rect(offset, 0, offset + kNumberWidth, kNumberHeight);
+ Common::Rect dstRect = Common::Rect(numCount * kNumberWidth + kDstX, kDstY, (numCount + 1) * kNumberWidth + kDstX, kDstY + kNumberHeight);
+ _vm->_gfx->drawImageRect(numCount + 364, srcRect, dstRect);
+ numCount++;
+ }
+ }
+
+ assert(numCount == 5); // Sanity check
}
}
void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("blabbook");
+ uint32 *page = _vm->getVar("blabbook");
// Decrement the page if it's not the first page
if (*page == 1)
@@ -460,7 +698,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
(*page)--;
// Play the page turning sound
- _vm->_sound->playSound(22, false);
+ _vm->_sound->playSound(22);
// Now update the screen :)
_vm->_gfx->scheduleTransition(1);
@@ -469,7 +707,7 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("blabbook");
+ uint32 *page = _vm->getVar("blabbook");
// Increment the page if it's not the last page
if (*page == 22)
@@ -477,7 +715,7 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
(*page)++;
// Play the page turning sound
- _vm->_sound->playSound(23, false);
+ _vm->_sound->playSound(23);
// Now update the screen :)
_vm->_gfx->scheduleTransition(0);
@@ -485,8 +723,8 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
}
void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
- uint32 heat = *_vm->matchVarToString("bheat");
- uint32 boilerInactive = *_vm->matchVarToString("bcratergg");
+ uint32 heat = *_vm->getVar("bheat");
+ uint32 boilerInactive = *_vm->getVar("bcratergg");
if (heat != 0)
_vm->_sound->playSLST(1, _vm->getCurCard());
@@ -497,58 +735,64 @@ void RivenExternal::xsoundplug(uint16 argc, uint16 *argv) {
}
void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
- uint32 heat = *_vm->matchVarToString("bheat");
- uint32 water = *_vm->matchVarToString("bblrwtr");
- uint32 platform = *_vm->matchVarToString("bblrgrt");
+ uint32 heat = *_vm->getVar("bheat");
+ uint32 water = *_vm->getVar("bblrwtr");
+ uint32 platform = *_vm->getVar("bblrgrt");
+
+ // Stop any background videos
+ _vm->_video->stopVideos();
if (argv[0] == 1) {
+ // Water is filling/draining from the boiler
if (water == 0) {
- if (platform == 0)
- _vm->_video->activateMLST(10, _vm->getCurCard());
- else
+ if (platform == 1)
_vm->_video->activateMLST(12, _vm->getCurCard());
- } else if (heat == 0) {
- if (platform == 0)
- _vm->_video->activateMLST(19, _vm->getCurCard());
else
+ _vm->_video->activateMLST(10, _vm->getCurCard());
+ } else if (heat == 1) {
+ if (platform == 1)
_vm->_video->activateMLST(22, _vm->getCurCard());
- } else {
- if (platform == 0)
- _vm->_video->activateMLST(13, _vm->getCurCard());
else
+ _vm->_video->activateMLST(19, _vm->getCurCard());
+ } else {
+ if (platform == 1)
_vm->_video->activateMLST(16, _vm->getCurCard());
+ else
+ _vm->_video->activateMLST(13, _vm->getCurCard());
}
} else if (argv[0] == 2 && water != 0) {
- if (heat == 0) {
- if (platform == 0)
- _vm->_video->activateMLST(20, _vm->getCurCard());
- else
+ if (heat == 1) {
+ // Turning on the heat
+ if (platform == 1)
_vm->_video->activateMLST(23, _vm->getCurCard());
+ else
+ _vm->_video->activateMLST(20, _vm->getCurCard());
} else {
- if (platform == 0)
+ // Turning off the heat
+ if (platform == 1)
_vm->_video->activateMLST(18, _vm->getCurCard());
else
_vm->_video->activateMLST(15, _vm->getCurCard());
}
} else if (argv[0] == 3) {
- if (platform == 0) {
- if (water == 0) {
- _vm->_video->activateMLST(11, _vm->getCurCard());
- } else {
- if (heat == 0)
- _vm->_video->activateMLST(17, _vm->getCurCard());
- else
+ if (platform == 1) {
+ // Lowering the platform
+ if (water == 1) {
+ if (heat == 1)
_vm->_video->activateMLST(24, _vm->getCurCard());
- }
- } else {
- if (water == 0) {
- _vm->_video->activateMLST(9, _vm->getCurCard());
- } else {
- if (heat == 0)
- _vm->_video->activateMLST(14, _vm->getCurCard());
else
+ _vm->_video->activateMLST(17, _vm->getCurCard());
+ } else
+ _vm->_video->activateMLST(11, _vm->getCurCard());
+ } else {
+ // Raising the platform
+ if (water == 1) {
+ if (heat == 1)
_vm->_video->activateMLST(21, _vm->getCurCard());
- }
+ else
+ _vm->_video->activateMLST(14, _vm->getCurCard());
+ } else
+ _vm->_video->activateMLST(9, _vm->getCurCard());
}
}
@@ -557,27 +801,26 @@ void RivenExternal::xbchangeboiler(uint16 argc, uint16 *argv) {
else if (argv[0] == 2)
_vm->_sound->playSLST(1, _vm->getCurCard());
- _vm->_video->playMovie(11);
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(11);
}
void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) {
- uint32 heat = *_vm->matchVarToString("bheat");
- uint32 platform = *_vm->matchVarToString("bblrgrt");
+ uint32 heat = *_vm->getVar("bheat");
+ uint32 platform = *_vm->getVar("bblrgrt");
if (heat) {
if (platform == 0) {
- _vm->_video->activateMLST(7, _vm->getCurCard());
- _vm->_video->playMovie(7);
- } else {
_vm->_video->activateMLST(8, _vm->getCurCard());
_vm->_video->playMovie(8);
+ } else {
+ _vm->_video->activateMLST(7, _vm->getCurCard());
+ _vm->_video->playMovie(7);
}
} else {
- _vm->_video->stopMovie(7);
- _vm->_video->stopMovie(8);
+ _vm->_video->disableMovie(7);
+ _vm->_video->disableMovie(8);
}
-
- _vm->refreshCard();
}
void RivenExternal::xbsettrap(uint16 argc, uint16 *argv) {
@@ -613,7 +856,7 @@ void RivenExternal::xbait(uint16 argc, uint16 *argv) {
// Set the bait if we put it on the plate
if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
- *_vm->matchVarToString("bbait") = 1;
+ *_vm->getVar("bbait") = 1;
_vm->_gfx->drawPLST(4);
_vm->_gfx->updateScreen();
_vm->_hotspots[3].enabled = false; // Disable bait hotspot
@@ -652,32 +895,32 @@ void RivenExternal::xbaitplate(uint16 argc, uint16 *argv) {
// Set the bait if we put it on the plate, remove otherwise
if (_vm->_hotspots[9].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
- *_vm->matchVarToString("bbait") = 1;
+ *_vm->getVar("bbait") = 1;
_vm->_gfx->drawPLST(4);
_vm->_gfx->updateScreen();
_vm->_hotspots[3].enabled = false; // Disable bait hotspot
_vm->_hotspots[9].enabled = true; // Enable baitplate hotspot
} else {
- *_vm->matchVarToString("bbait") = 0;
+ *_vm->getVar("bbait") = 0;
_vm->_hotspots[3].enabled = true; // Enable bait hotspot
_vm->_hotspots[9].enabled = false; // Disable baitplate hotspot
}
}
void RivenExternal::xbisland190_opencard(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkDomeSliders(27, 28);
}
void RivenExternal::xbisland190_resetsliders(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ resetDomeSliders(701, 41, 2);
}
void RivenExternal::xbisland190_slidermd(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ dragDomeSlider(701, 41, 27, 28, 2);
}
void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkSliderCursorChange(2);
}
void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) {
@@ -689,15 +932,17 @@ void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) {
}
void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
+ Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
+
// Get the variable for the valve
- uint32 *valve = _vm->matchVarToString("bvalve");
+ uint32 *valve = _vm->getVar("bvalve");
int changeX = 0;
int changeY = 0;
bool done = false;
// Set the cursor to the closed position
- _vm->_gfx->changeCursor(2004);
+ _vm->_gfx->changeCursor(kRivenClosedHandCursor);
_vm->_system->updateScreen();
while (!done) {
@@ -706,8 +951,8 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
while (_vm->_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
- changeX = event.mouse.x - _vm->_mousePos.x;
- changeY = _vm->_mousePos.y - event.mouse.y;
+ changeX = event.mouse.x - startPos.x;
+ changeY = startPos.y - event.mouse.y;
_vm->_system->updateScreen();
break;
case Common::EVENT_LBUTTONUP:
@@ -746,26 +991,26 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// 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 (*_vm->getVar("bidvlv") == 1) { // Check which way the water is going at the boiler
+ if (*_vm->getVar("bblrarm") == 1) {
// If the pipe is open, make sure the water is drained out
- *_vm->matchVarToString("bheat") = 0;
- *_vm->matchVarToString("bblrwtr") = 0;
+ *_vm->getVar("bheat") = 0;
+ *_vm->getVar("bblrwtr") = 0;
} else {
// If the pipe is closed, fill the boiler again
- *_vm->matchVarToString("bheat") = *_vm->matchVarToString("bblrvalve");
- *_vm->matchVarToString("bblrwtr") = 1;
+ *_vm->getVar("bheat") = *_vm->getVar("bblrvalve");
+ *_vm->getVar("bblrwtr") = 1;
}
} else {
// Have the grating inside the boiler match the switch outside
- *_vm->matchVarToString("bblrgrt") = (*_vm->matchVarToString("bblrsw") == 1) ? 0 : 1;
+ *_vm->getVar("bblrgrt") = (*_vm->getVar("bblrsw") == 1) ? 0 : 1;
}
}
}
void RivenExternal::xbchipper(uint16 argc, uint16 *argv) {
// Why is this an external command....?
- if (*_vm->matchVarToString("bvalve") == 2)
+ if (*_vm->getVar("bvalve") == 2)
_vm->_video->playMovieBlocking(2);
}
@@ -786,19 +1031,19 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) {
}
void RivenExternal::xgisland25_opencard(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkDomeSliders(29, 30);
}
void RivenExternal::xgisland25_resetsliders(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ resetDomeSliders(161, 16, 2);
}
void RivenExternal::xgisland25_slidermd(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ dragDomeSlider(161, 16, 29, 30, 2);
}
void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkSliderCursorChange(2);
}
void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) {
@@ -810,18 +1055,20 @@ void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) {
}
void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) {
- // TODO: "Bubble" map related
+ // Play the deactivation of a pool if one is active and a different one is activated
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(*_vm->getVar("glkbtns") * 2);
}
void RivenExternal::xgwt200_scribetime(uint16 argc, uint16 *argv) {
// Get the current time
- *_vm->matchVarToString("gscribetime") = _vm->_system->getMillis();
+ *_vm->getVar("gscribetime") = _vm->_system->getMillis();
}
void RivenExternal::xgwt900_scribe(uint16 argc, uint16 *argv) {
- uint32 *scribeVar = _vm->matchVarToString("gscribe");
+ uint32 *scribeVar = _vm->getVar("gscribe");
- if (*scribeVar == 1 && _vm->_system->getMillis() > *_vm->matchVarToString("gscribetime") + 40000)
+ if (*scribeVar == 1 && _vm->_system->getMillis() > *_vm->getVar("gscribetime") + 40000)
*scribeVar = 2;
}
@@ -863,9 +1110,9 @@ void RivenExternal::xglview_villageon(uint16 argc, uint16 *argv) {
void RivenExternal::xreseticons(uint16 argc, uint16 *argv) {
// Reset the icons when going to Tay (rspit)
- *_vm->matchVarToString("jicons") = 0;
- *_vm->matchVarToString("jiconorder") = 0;
- *_vm->matchVarToString("jrbook") = 0;
+ *_vm->getVar("jicons") = 0;
+ *_vm->getVar("jiconorder") = 0;
+ *_vm->getVar("jrbook") = 0;
}
// Count up how many icons are pressed
@@ -886,30 +1133,30 @@ static byte countDepressedIcons(uint32 iconOrderVar) {
void RivenExternal::xicon(uint16 argc, uint16 *argv) {
// Set atemp as the status of whether or not the icon can be depressed.
- if (*_vm->matchVarToString("jicons") & (1 << (argv[0] - 1))) {
+ if (*_vm->getVar("jicons") & (1 << (argv[0] - 1))) {
// This icon is depressed. Allow depression only if the last depressed icon was this one.
- if ((*_vm->matchVarToString("jiconorder") & 0x1f) == argv[0])
- *_vm->matchVarToString("atemp") = 1;
+ if ((*_vm->getVar("jiconorder") & 0x1f) == argv[0])
+ *_vm->getVar("atemp") = 1;
else
- *_vm->matchVarToString("atemp") = 2;
+ *_vm->getVar("atemp") = 2;
} else
- *_vm->matchVarToString("atemp") = 0;
+ *_vm->getVar("atemp") = 0;
}
void RivenExternal::xcheckicons(uint16 argc, uint16 *argv) {
// Reset the icons if this is the sixth icon
- uint32 *iconOrderVar = _vm->matchVarToString("jiconorder");
+ uint32 *iconOrderVar = _vm->getVar("jiconorder");
if (countDepressedIcons(*iconOrderVar) == 5) {
*iconOrderVar = 0;
- *_vm->matchVarToString("jicons") = 0;
- _vm->_sound->playSound(46, false);
+ *_vm->getVar("jicons") = 0;
+ _vm->_sound->playSound(46);
}
}
void RivenExternal::xtoggleicon(uint16 argc, uint16 *argv) {
// Get the variables
- uint32 *iconsDepressed = _vm->matchVarToString("jicons");
- uint32 *iconOrderVar = _vm->matchVarToString("jiconorder");
+ uint32 *iconsDepressed = _vm->getVar("jicons");
+ uint32 *iconOrderVar = _vm->getVar("jiconorder");
if (*iconsDepressed & (1 << (argv[0] - 1))) {
// The icon is depressed, now unpress it
@@ -922,13 +1169,13 @@ void RivenExternal::xtoggleicon(uint16 argc, uint16 *argv) {
}
// Check if the puzzle is complete now and assign 1 to jrbook if the puzzle is complete.
- if (*iconOrderVar == *_vm->matchVarToString("jiconcorrectorder"))
- *_vm->matchVarToString("jrbook") = 1;
+ if (*iconOrderVar == *_vm->getVar("jiconcorrectorder"))
+ *_vm->getVar("jrbook") = 1;
}
void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
- uint32 iconsDepressed = *_vm->matchVarToString("jicons");
+ uint32 iconsDepressed = *_vm->getVar("jicons");
// Now, draw which icons are depressed based on the bits of the variable
if (iconsDepressed & (1 << 0))
@@ -949,7 +1196,7 @@ void RivenExternal::xjtunnel103_pictfix(uint16 argc, uint16 *argv) {
void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
- uint32 iconsDepressed = *_vm->matchVarToString("jicons");
+ uint32 iconsDepressed = *_vm->getVar("jicons");
// Now, draw which icons are depressed based on the bits of the variable
if (iconsDepressed & (1 << 9))
@@ -972,7 +1219,7 @@ void RivenExternal::xjtunnel104_pictfix(uint16 argc, uint16 *argv) {
void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
- uint32 iconsDepressed = *_vm->matchVarToString("jicons");
+ uint32 iconsDepressed = *_vm->getVar("jicons");
// Now, draw which icons are depressed based on the bits of the variable
if (iconsDepressed & (1 << 3))
@@ -993,7 +1240,7 @@ void RivenExternal::xjtunnel105_pictfix(uint16 argc, uint16 *argv) {
void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
// Get the jicons variable which contains which of the stones are depressed in the rebel tunnel puzzle
- uint32 iconsDepressed = *_vm->matchVarToString("jicons");
+ uint32 iconsDepressed = *_vm->getVar("jicons");
// Now, draw which icons are depressed based on the bits of the variable
if (iconsDepressed & (1 << 16))
@@ -1027,7 +1274,7 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
_vm->changeToCard(_vm->matchRMAPToCard(0x183a9)); // Change to card looking straight again
_vm->_video->playMovieBlocking(2);
- uint32 *gallows = _vm->matchVarToString("jgallows");
+ uint32 *gallows = _vm->getVar("jgallows");
if (*gallows == 1) {
// If the gallows is open, play the up movie and return
_vm->_video->playMovieBlocking(3);
@@ -1072,15 +1319,15 @@ void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
}
void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ resetDomeSliders(_vm->getFeatures() & GF_DVD ? 547 : 548, 81, 2);
}
void RivenExternal::xjdome25_slidermd(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ dragDomeSlider(_vm->getFeatures() & GF_DVD ? 547: 548, 81, 29, 28, 2);
}
void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkSliderCursorChange(2);
}
void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) {
@@ -1092,18 +1339,20 @@ void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) {
}
int RivenExternal::jspitElevatorLoop() {
+ Common::Point startPos = _vm->_system->getEventManager()->getMousePos();
+
Common::Event event;
int changeLevel = 0;
- _vm->_gfx->changeCursor(2004);
+ _vm->_gfx->changeCursor(kRivenClosedHandCursor);
_vm->_system->updateScreen();
for (;;) {
while (_vm->_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
- if (event.mouse.y > (_vm->_mousePos.y + 10)) {
+ if (event.mouse.y > (startPos.y + 10)) {
changeLevel = -1;
- } else if (event.mouse.y < (_vm->_mousePos.y - 10)) {
+ } else if (event.mouse.y < (startPos.y - 10)) {
changeLevel = 1;
} else {
changeLevel = 0;
@@ -1157,7 +1406,7 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
_vm->_video->playMovieBlocking(6);
// If the whark's mouth is open, close it
- uint32 *mouthVar = _vm->matchVarToString("jwmouth");
+ uint32 *mouthVar = _vm->getVar("jwmouth");
if (*mouthVar == 1) {
_vm->_video->playMovieBlocking(3);
_vm->_video->playMovieBlocking(8);
@@ -1176,27 +1425,27 @@ void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
void RivenExternal::xjplaybeetle_550(uint16 argc, uint16 *argv) {
// Play a beetle animation 25% of the time
- *_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+ *_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
}
void RivenExternal::xjplaybeetle_600(uint16 argc, uint16 *argv) {
// Play a beetle animation 25% of the time
- *_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+ *_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
}
void RivenExternal::xjplaybeetle_950(uint16 argc, uint16 *argv) {
// Play a beetle animation 25% of the time
- *_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+ *_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
}
void RivenExternal::xjplaybeetle_1050(uint16 argc, uint16 *argv) {
// Play a beetle animation 25% of the time
- *_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
+ *_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0) ? 1 : 0;
}
void RivenExternal::xjplaybeetle_1450(uint16 argc, uint16 *argv) {
// Play a beetle animation 25% of the time as long as the girl is not present
- *_vm->matchVarToString("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && *_vm->matchVarToString("jgirl") != 1) ? 1 : 0;
+ *_vm->getVar("jplaybeetle") = (_vm->_rnd->getRandomNumberRng(0, 3) == 0 && *_vm->getVar("jgirl") != 1) ? 1 : 0;
}
void RivenExternal::xjlagoon700_alert(uint16 argc, uint16 *argv) {
@@ -1209,7 +1458,7 @@ void RivenExternal::xjlagoon800_alert(uint16 argc, uint16 *argv) {
void RivenExternal::xjlagoon1500_alert(uint16 argc, uint16 *argv) {
// Have the sunners move a bit as you get closer ;)
- uint32 *sunners = _vm->matchVarToString("jsunners");
+ uint32 *sunners = _vm->getVar("jsunners");
if (*sunners == 0) {
_vm->_video->playMovieBlocking(3);
} else if (*sunners == 1) {
@@ -1234,14 +1483,14 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) {
// WORKAROUND: The special change stuff only handles one destination and it would
// be messy to modify the way that currently works. If we use the trap book on Tay,
// we should be using the Tay end game sequences.
- if (*_vm->matchVarToString("returnstackid") == rspit) {
+ if (*_vm->getVar("returnstackid") == rspit) {
_vm->changeToStack(rspit);
_vm->changeToCard(2);
return;
}
// You used the trap book... why? What were you thinking?
- uint32 *gehnState = _vm->matchVarToString("agehn");
+ uint32 *gehnState = _vm->getVar("agehn");
if (*gehnState == 0) // Gehn who?
runEndGame(1);
@@ -1252,16 +1501,127 @@ void RivenExternal::xorollcredittime(uint16 argc, uint16 *argv) {
}
void RivenExternal::xbookclick(uint16 argc, uint16 *argv) {
- // TODO: This fun external command is probably one of the most complex,
- // up there with the marble puzzle ones. It involves so much... Basically,
- // it's playing when Gehn holds the trap book up to you and you have to
- // click on the book (hence the name of the function). Yeah, not fun.
- // Lots of timing stuff needs to be done for a couple videos.
+ // Hide the cursor
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+
+ // Let's hook onto our video
+ VideoHandle video = _vm->_video->findVideoHandle(argv[0]);
+
+ // Convert from the standard QuickTime base time to milliseconds
+ // The values are in terms of 1/600 of a second.
+ // Have I said how much I just *love* QuickTime? </sarcasm>
+ uint32 startTime = argv[1] * 1000 / 600;
+ uint32 endTime = argv[2] * 1000 / 600;
+
+ // Track down our hotspot
+ // Of course, they're not in any sane order...
+ static const uint16 hotspotMap[] = { 1, 3, 2, 0 };
+ Common::Rect hotspotRect = _vm->_hotspots[hotspotMap[argv[3] - 1]].rect;
+
+ debug(0, "xbookclick:");
+ debug(0, "\tVideo Code = %d", argv[0]);
+ debug(0, "\tStart Time = %dms", startTime);
+ debug(0, "\tEnd Time = %dms", endTime);
+ debug(0, "\tHotspot = %d -> %d", argv[3], hotspotMap[argv[3] - 1]);
+
+ // Just let the video play while we wait until Gehn opens the trap book for us
+ while (_vm->_video->getElapsedTime(video) < startTime && !_vm->shouldQuit()) {
+ if (_vm->_video->updateBackgroundMovies())
+ _vm->_system->updateScreen();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event))
+ ;
+
+ _vm->_system->delayMillis(10);
+ }
+
+ // Break out if we're quitting
+ if (_vm->shouldQuit())
+ return;
+
+ // Update our hotspot stuff
+ if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
+ _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ else
+ _vm->_gfx->changeCursor(kRivenMainCursor);
+
+ // OK, Gehn has opened the trap book and has asked us to go in. Let's watch
+ // and see what the player will do...
+ while (_vm->_video->getElapsedTime(video) < endTime && !_vm->shouldQuit()) {
+ bool updateScreen = _vm->_video->updateBackgroundMovies();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos()))
+ _vm->_gfx->changeCursor(kRivenOpenHandCursor);
+ else
+ _vm->_gfx->changeCursor(kRivenMainCursor);
+ updateScreen = false; // Don't update twice, changing the cursor already updates the screen
+ break;
+ case Common::EVENT_LBUTTONUP:
+ if (hotspotRect.contains(_vm->_system->getEventManager()->getMousePos())) {
+ // OK, we've used the trap book! We go for ride lady!
+ _vm->_scriptMan->stopAllScripts(); // Stop all running scripts (so we don't remain in the cage)
+ _vm->_video->stopVideos(); // Stop all videos
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_gfx->drawPLST(3); // Black out the screen
+ _vm->_gfx->updateScreen(); // Update the screen
+ _vm->_sound->playSound(0); // Play the link sound
+ _vm->_video->activateMLST(7, _vm->getCurCard()); // Activate Gehn Link Video
+ _vm->_video->playMovieBlocking(1); // Play Gehn Link Video
+ *_vm->getVar("agehn") = 4; // Set Gehn to the trapped state
+ *_vm->getVar("atrapbook") = 1; // We've got the trap book again
+ _vm->_sound->playSound(0); // Play the link sound again
+ _vm->changeToCard(_vm->matchRMAPToCard(0x2885)); // Link out! (TODO: Shouldn't this card change?)
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (updateScreen && !_vm->shouldQuit())
+ _vm->_system->updateScreen();
+
+ _vm->_system->delayMillis(10);
+ }
+
+ // Break out if we're quitting
+ if (_vm->shouldQuit())
+ return;
+
+ // Hide the cursor again
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+
+ // If there was no click and this is the third time Gehn asks us to
+ // use the trap book, he will shoot the player. Dead on arrival.
+ // Run the credits from here.
+ if (*_vm->getVar("agehn") == 3) {
+ _vm->_scriptMan->stopAllScripts();
+ runCredits(argv[0]);
+ return;
+ }
+
+ // There was no click, so just play the rest of the video.
+ while (!_vm->_video->endOfVideo(video) && !_vm->shouldQuit()) {
+ if (_vm->_video->updateBackgroundMovies())
+ _vm->_system->updateScreen();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event))
+ ;
+
+ _vm->_system->delayMillis(10);
+ }
}
void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
// Close the blank linking book if it's open
- uint32 *book = _vm->matchVarToString("odeskbook");
+ uint32 *book = _vm->getVar("odeskbook");
if (*book != 1)
return;
@@ -1284,16 +1644,16 @@ void RivenExternal::xooffice30_closebook(uint16 argc, uint16 *argv) {
void RivenExternal::xobedroom5_closedrawer(uint16 argc, uint16 *argv) {
// Close the drawer if open when clicking on the journal.
_vm->_video->playMovieBlocking(2);
- *_vm->matchVarToString("ostanddrawer") = 0;
+ *_vm->getVar("ostanddrawer") = 0;
}
void RivenExternal::xogehnopenbook(uint16 argc, uint16 *argv) {
- _vm->_gfx->drawPLST(*_vm->matchVarToString("ogehnpage"));
+ _vm->_gfx->drawPLST(*_vm->getVar("ogehnpage"));
}
void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("ogehnpage");
+ uint32 *page = _vm->getVar("ogehnpage");
// Decrement the page if it's not the first page
if (*page == 1)
@@ -1301,7 +1661,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
(*page)--;
// Play the page turning sound
- _vm->_sound->playSound(12, false);
+ _vm->_sound->playSound(12);
// Now update the screen :)
_vm->_gfx->scheduleTransition(1);
@@ -1310,7 +1670,7 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
// Get the page variable
- uint32 *page = _vm->matchVarToString("ogehnpage");
+ uint32 *page = _vm->getVar("ogehnpage");
// Increment the page if it's not the last page
if (*page == 13)
@@ -1318,7 +1678,7 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
(*page)++;
// Play the page turning sound
- _vm->_sound->playSound(13, false);
+ _vm->_sound->playSound(13);
// Now update the screen :)
_vm->_gfx->scheduleTransition(0);
@@ -1334,7 +1694,7 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
// Hide the cursor
_vm->_gfx->changeCursor(kRivenHideCursor);
- uint32 *prisonCombo = _vm->matchVarToString("pcorrectorder");
+ uint32 *prisonCombo = _vm->getVar("pcorrectorder");
uint32 soundTime = _vm->_system->getMillis() - 500; // Start the first sound instantly
byte curSound = 0;
@@ -1371,19 +1731,19 @@ void RivenExternal::xgwatch(uint16 argc, uint16 *argv) {
void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) {
// Play button sound based on argv[0]
- _vm->_sound->playSound(argv[0] + 5, false);
+ _vm->_sound->playSound(argv[0] + 5);
// It is impossible to get here if Gehn is not trapped. However,
// the original also disallows brute forcing the ending if you have
// not yet trapped Gehn.
- if (*_vm->matchVarToString("agehn") != 4)
+ if (*_vm->getVar("agehn") != 4)
return;
- uint32 *correctDigits = _vm->matchVarToString("pelevcombo");
+ uint32 *correctDigits = _vm->getVar("pelevcombo");
// pelevcombo keeps count of how many buttons we have pressed in the correct order.
// When pelevcombo is 5, clicking the handle will show the video freeing Catherine.
- if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->matchVarToString("pcorrectorder"), *correctDigits))
+ if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->getVar("pcorrectorder"), *correctDigits))
*correctDigits += 1;
else
*correctDigits = 0;
@@ -1398,19 +1758,19 @@ void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) {
}
void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkDomeSliders(31, 5);
}
void RivenExternal::xpisland25_resetsliders(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ resetDomeSliders(58, 10, 6);
}
void RivenExternal::xpisland25_slidermd(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ dragDomeSlider(58, 10, 31, 5, 6);
}
void RivenExternal::xpisland25_slidermw(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkSliderCursorChange(6);
}
// ------------------------------------------------------------------------------------
@@ -1430,8 +1790,8 @@ void RivenExternal::xrcredittime(uint16 argc, uint16 *argv) {
void RivenExternal::xrshowinventory(uint16 argc, uint16 *argv) {
// Give the trap book and Catherine's journal to the player
- *_vm->matchVarToString("atrapbook") = 1;
- *_vm->matchVarToString("acathbook") = 1;
+ *_vm->getVar("atrapbook") = 1;
+ *_vm->getVar("acathbook") = 1;
_vm->_gfx->showInventory();
}
@@ -1452,29 +1812,29 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
_vm->_video->playMovieBlocking(3);
// Don't do anything else if the telescope power is off
- if (*_vm->matchVarToString("ttelevalve") == 0)
+ if (*_vm->getVar("ttelevalve") == 0)
return;
- uint32 *telescopePos = _vm->matchVarToString("ttelescope");
- uint32 *telescopeCover = _vm->matchVarToString("ttelecover");
+ uint32 *telescopePos = _vm->getVar("ttelescope");
+ uint32 *telescopeCover = _vm->getVar("ttelecover");
if (*telescopePos == 1) {
// We're at the bottom, which means one of two things can happen...
- if (*telescopeCover == 1 && *_vm->matchVarToString("ttelepin") == 1) {
+ if (*telescopeCover == 1 && *_vm->getVar("ttelepin") == 1) {
// ...if the cover is open and the pin is up, the game is now over.
- if (*_vm->matchVarToString("pcage") == 2) {
+ if (*_vm->getVar("pcage") == 2) {
// The best ending: Catherine is free, Gehn is trapped, Atrus comes to rescue you.
// And now we fall back to Earth... all the way...
warning("xtexterior300_telescopedown: Good ending");
_vm->_video->activateMLST(8, _vm->getCurCard());
runEndGame(8);
- } else if (*_vm->matchVarToString("agehn") == 4) {
+ } else if (*_vm->getVar("agehn") == 4) {
// The ok ending: Catherine is still trapped, Gehn is trapped, Atrus comes to rescue you.
// Nice going! Catherine and the islanders are all dead now! Just go back to your home...
warning("xtexterior300_telescopedown: OK ending");
_vm->_video->activateMLST(9, _vm->getCurCard());
runEndGame(9);
- } else if (*_vm->matchVarToString("atrapbook") == 1) {
+ } else if (*_vm->getVar("atrapbook") == 1) {
// The bad ending: Catherine is trapped, Gehn is free, Atrus gets shot by Gehn,
// And then you get shot by Cho. Nice going! Catherine and the islanders are dead
// and you have just set Gehn free from Riven, not to mention you're dead.
@@ -1492,7 +1852,9 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) {
}
} else {
// ...the telescope can't move down anymore.
- // TODO: Play sound
+ // Play the sound of not being able to move
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_sound->playSoundBlocking(13);
}
} else {
// We're not at the bottom, and we can move down again
@@ -1510,14 +1872,16 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
_vm->_video->playMovieBlocking(3);
// Don't do anything else if the telescope power is off
- if (*_vm->matchVarToString("ttelevalve") == 0)
+ if (*_vm->getVar("ttelevalve") == 0)
return;
- uint32 *telescopePos = _vm->matchVarToString("ttelescope");
+ uint32 *telescopePos = _vm->getVar("ttelescope");
// Check if we can't move up anymore
if (*telescopePos == 5) {
- // TODO: Play sound
+ // Play the sound of not being able to move
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_sound->playSoundBlocking(13);
return;
}
@@ -1530,9 +1894,9 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) {
void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
// Called when clicking the telescope cover buttons. argv[0] is the button number (1...5).
- uint32 *correctDigits = _vm->matchVarToString("tcovercombo");
+ uint32 *correctDigits = _vm->getVar("tcovercombo");
- if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->matchVarToString("tcorrectorder"), *correctDigits))
+ if (*correctDigits < 5 && argv[0] == getComboDigit(*_vm->getVar("tcorrectorder"), *correctDigits))
*correctDigits += 1;
else
*correctDigits = 0;
@@ -1548,8 +1912,8 @@ void RivenExternal::xtisland390_covercombo(uint16 argc, uint16 *argv) {
// Atrus' Journal and Trap Book are added to inventory
void RivenExternal::xtatrusgivesbooks(uint16 argc, uint16 *argv) {
// Give the player Atrus' Journal and the Trap book
- *_vm->matchVarToString("aatrusbook") = 1;
- *_vm->matchVarToString("atrapbook") = 1;
+ *_vm->getVar("aatrusbook") = 1;
+ *_vm->getVar("atrapbook") = 1;
}
// Trap Book is removed from inventory
@@ -1557,35 +1921,216 @@ void RivenExternal::xtchotakesbook(uint16 argc, uint16 *argv) {
// And now Cho takes the trap book. Sure, this isn't strictly
// necessary to add and them remove the trap book... but it
// seems better to do this ;)
- *_vm->matchVarToString("atrapbook") = 0;
+ *_vm->getVar("atrapbook") = 0;
}
void RivenExternal::xthideinventory(uint16 argc, uint16 *argv) {
_vm->_gfx->hideInventory();
}
+// Marble Puzzle related constants
+static const uint32 kMarbleCount = 6;
+static const int kSmallMarbleWidth = 4;
+static const int kSmallMarbleHeight = 2;
+static const int kLargeMarbleSize = 8;
+static const int kMarbleHotspotSize = 13;
+static const char *s_marbleNames[] = { "tred", "torange", "tyellow", "tgreen", "tblue", "tviolet" };
+
+// Marble Puzzle helper functions
+// The y portion takes the upper 16 bits, while the x portion takes the lower 16 bits
+static void setMarbleX(uint32 *var, byte x) {
+ *var = (*var & 0xff00) | (x + 1);
+}
+
+static void setMarbleY(uint32 *var, byte y) {
+ *var = ((y + 1) << 16) | (*var & 0xff);
+}
+
+static byte getMarbleX(uint32 *var) {
+ return (*var & 0xff) - 1;
+}
+
+static byte getMarbleY(uint32 *var) { // Give that that Y you old hag! </bad Seinfeld reference>
+ return ((*var >> 16) & 0xff) - 1;
+}
+
+static Common::Rect generateMarbleGridRect(uint16 x, uint16 y) {
+ // x/y in terms of 0!
+ static const int marbleGridOffsetX[] = { 134, 202, 270, 338, 406 };
+ static const int marbleGridOffsetY[] = { 24, 92, 159, 227, 295 };
+
+ uint16 offsetX = marbleGridOffsetX[x / 5] + (x % 5) * kMarbleHotspotSize;
+ uint16 offsetY = marbleGridOffsetY[y / 5] + (y % 5) * kMarbleHotspotSize;
+ return Common::Rect(offsetX, offsetY, offsetX + kMarbleHotspotSize, offsetY + kMarbleHotspotSize);
+}
+
void RivenExternal::xt7500_checkmarbles(uint16 argc, uint16 *argv) {
- // TODO: Lots of stuff to do here, eventually we have to check each individual
- // marble position and set apower based on that. The game handles the video playing
- // so we don't have to. For the purposes of making the game progress further, we'll
- // just turn the power on for now.
- *_vm->matchVarToString("apower") = 1;
+ // Set apower if the marbles are in their correct spot.
+
+ bool valid = true;
+ static const uint32 marbleFinalValues[] = { 1114121, 1441798, 0, 65552, 65558, 262146 };
+
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ if (*_vm->getVar(s_marbleNames[i]) != marbleFinalValues[i]) {
+ valid = false;
+ break;
+ }
+
+ // If we have the correct combo, activate the power and reset the marble positions
+ // Otherwise, make sure the power is off
+ if (valid) {
+ *_vm->getVar("apower") = 1;
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ *_vm->getVar(s_marbleNames[i]) = 0;
+ } else
+ *_vm->getVar("apower") = 0;
}
void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) {
- // TODO: Marble puzzle related
+ // Draw the small marbles when we're a step away from the waffle
+ uint16 baseBitmapId = (_vm->getFeatures() & GF_DVD) ? 539 : 526;
+ bool waffleDown = *_vm->getVar("twaffle") != 0;
+
+ // Note that each of the small marble images is exactly 4x2
+
+ for (uint16 i = 0; i < kMarbleCount; i++) {
+ uint32 *var = _vm->getVar(s_marbleNames[i]);
+
+ if (*var == 0) {
+ // The marble is still in its initial place
+ // (Note that this is still drawn even if the waffle is down)
+ int marbleX = 376 + i * 2;
+ int marbleY = 253 + i * 4;
+ _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight);
+ } else if (waffleDown) {
+ // The marble is on the grid and the waffle is down
+ // (Nothing to draw here)
+ } else {
+ // The marble is on the grid and the waffle is up
+ // TODO: Draw them onto the grid
+ }
+ }
+}
+
+void RivenExternal::setMarbleHotspots() {
+ // Set the hotspots
+ for (uint16 i = 0; i < kMarbleCount; i++) {
+ uint32 *marblePos = _vm->getVar(s_marbleNames[i]);
+
+ if (*marblePos == 0) // In the receptacle
+ _vm->_hotspots[i + 3].rect = _marbleBaseHotspots[i];
+ else // On the grid
+ _vm->_hotspots[i + 3].rect = generateMarbleGridRect(getMarbleX(marblePos), getMarbleY(marblePos));
+ }
}
void RivenExternal::xt7800_setup(uint16 argc, uint16 *argv) {
- // TODO: Marble puzzle related
+ // First, let's store the base receptacle hotspots for the marbles
+ if (_marbleBaseHotspots.empty())
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ _marbleBaseHotspots.push_back(_vm->_hotspots[i + 3].rect);
+
+ // Move the marble hotspots based on their position variables
+ setMarbleHotspots();
+ *_vm->getVar("themarble") = 0;
+}
+
+void RivenExternal::drawMarbles() {
+ for (uint32 i = 0; i < kMarbleCount; i++) {
+ // Don't draw the marble if we're holding it
+ if (*_vm->getVar("themarble") - 1 == i)
+ continue;
+
+ Common::Rect rect = _vm->_hotspots[i + 3].rect;
+ // Trim the rect down a bit
+ rect.left += 3;
+ rect.top += 3;
+ rect.right -= 2;
+ rect.bottom -= 2;
+ _vm->_gfx->drawExtrasImage(i + 200, rect);
+ }
}
void RivenExternal::xdrawmarbles(uint16 argc, uint16 *argv) {
- // TODO: Marble puzzle related
+ // Draw marbles in the closeup
+ drawMarbles();
+
+ // We have to re-enable the updates here
+ // Would be really nice if the scripts did this for us, but alas...
+ _vm->_gfx->_updatesEnabled = true;
}
void RivenExternal::xtakeit(uint16 argc, uint16 *argv) {
- // TODO: Marble puzzle related
+ // Pick up and move a marble
+
+ // First, let's figure out what marble we're now holding
+ uint32 *marble = _vm->getVar("themarble");
+ *marble = 0;
+
+ for (uint32 i = 0; i < kMarbleCount; i++)
+ if (_vm->_hotspots[i + 3].rect.contains(_vm->_system->getEventManager()->getMousePos())) {
+ *marble = i + 1;
+ break;
+ }
+
+ // xtakeit() shouldn't be called if we're not on a marble hotspot
+ assert(*marble);
+
+ // Redraw the background
+ _vm->_gfx->drawPLST(1);
+ _vm->_gfx->updateScreen();
+
+ // Loop until the player lets go (or quits)
+ Common::Event event;
+ bool mouseDown = true;
+ while (mouseDown) {
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_LBUTTONUP)
+ mouseDown = false;
+ else if (event.type == Common::EVENT_MOUSEMOVE)
+ _vm->_system->updateScreen();
+ else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RTL)
+ return;
+ }
+
+ _vm->_system->delayMillis(10); // Take it easy on the CPU
+ }
+
+ // Check if we landed in a valid location and no other marble has that location
+ uint32 *marblePos = _vm->getVar(s_marbleNames[*marble - 1]);
+
+ bool foundMatch = false;
+ for (int y = 0; y < 25 && !foundMatch; y++) {
+ for (int x = 0; x < 25 && !foundMatch; x++) {
+ Common::Rect testHotspot = generateMarbleGridRect(x, y);
+
+ // Let's try to place the marble!
+ if (testHotspot.contains(_vm->_system->getEventManager()->getMousePos())) {
+ // Set this as the position
+ setMarbleX(marblePos, x);
+ setMarbleY(marblePos, y);
+
+ // Let's make sure no other marble is in this spot...
+ for (uint16 i = 0; i < kMarbleCount; i++)
+ if (i != *marble - 1 && *_vm->getVar(s_marbleNames[i]) == *marblePos)
+ *marblePos = 0;
+
+ // We have a match
+ foundMatch = true;
+ }
+ }
+ }
+
+ // If we still don't have a match, reset it to the original location
+ if (!foundMatch)
+ *marblePos = 0;
+
+ // Check the new hotspots and refresh everything
+ *marble = 0;
+ setMarbleHotspots();
+ _vm->_curHotspot = -1;
+ _vm->checkHotspotChange();
+ _vm->_gfx->updateScreen();
}
void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) {
@@ -1597,19 +2142,19 @@ void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) {
}
void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkDomeSliders(29, 30);
}
void RivenExternal::xtisland5056_resetsliders(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ resetDomeSliders(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 3);
}
void RivenExternal::xtisland5056_slidermd(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ dragDomeSlider(_vm->getFeatures() & GF_DVD ? 813 : 798, 37, 29, 30, 3);
}
void RivenExternal::xtisland5056_slidermw(uint16 argc, uint16 *argv) {
- // TODO: Dome related
+ checkSliderCursorChange(3);
}
void RivenExternal::xtatboundary(uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index bdf3fa01bc..1f012c82d9 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -39,9 +39,13 @@ public:
void runCommand(uint16 argc, uint16 *argv);
uint16 getComboDigit(uint32 correctCombo, uint32 digit);
+ uint32 getDomeSliderState() { return _sliderState; }
+ void setDomeSliderState(uint32 state) { _sliderState = state; }
private:
MohawkEngine_Riven *_vm;
+ uint32 _sliderState;
+ Common::Array<Common::Rect> _marbleBaseHotspots;
typedef void (RivenExternal::*ExternalCmd)(uint16 argc, uint16 *argv);
@@ -58,8 +62,16 @@ private:
int jspitElevatorLoop();
void runDemoBoundaryDialog();
void runEndGame(uint16 video);
+ void runCredits(uint16 video);
void runDomeCheck();
void runDomeButtonMovie();
+ void resetDomeSliders(uint16 bitmapId, uint16 soundId, uint16 startHotspot);
+ void checkDomeSliders(uint16 resetSlidersHotspot, uint16 openDomeHotspot);
+ void checkSliderCursorChange(uint16 startHotspot);
+ void dragDomeSlider(uint16 bitmapId, uint16 soundId, uint16 resetSlidersHotspot, uint16 openDomeHotspot, uint16 startHotspot);
+ void drawDomeSliders(uint16 bitmapId, uint16 startHotspot);
+ void drawMarbles();
+ void setMarbleHotspots();
// -----------------------------------------------------
// aspit (Main Menu, Books, Setup) external commands
@@ -86,6 +98,8 @@ private:
void xadisablemenureturn(uint16 argc, uint16 *argv);
void xaenablemenureturn(uint16 argc, uint16 *argv);
void xalaunchbrowser(uint16 argc, uint16 *argv);
+ void xadisablemenuintro(uint16 argc, uint16 *argv);
+ void xaenablemenuintro(uint16 argc, uint16 *argv);
// -----------------------------------------------------
// bspit (Boiler Island) external commands
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index d73b4ec0dc..c63a3f98fb 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -110,7 +110,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
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.");
+ warning("Incompatible saved game versions. No support for this yet");
delete mhk;
return false;
}
@@ -161,7 +161,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
if (name == "dropLeftStart" || name == "dropRightStart")
continue;
- uint32 *var = _vm->matchVarToString(name);
+ uint32 *var = _vm->getVar(name);
*var = rawVariables[i];
@@ -272,8 +272,8 @@ bool RivenSaveLoad::saveGame(Common::String filename) {
filename += ".rvn";
// Convert class variables to variable numbers
- *_vm->matchVarToString("currentstackid") = mapNewStackIDToOld(_vm->getCurStack());
- *_vm->matchVarToString("currentcardid") = _vm->getCurCard();
+ *_vm->getVar("currentstackid") = mapNewStackIDToOld(_vm->getCurStack());
+ *_vm->getVar("currentcardid") = _vm->getCurCard();
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
if (!saveFile)
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index 1fcaba8ac0..30d1d727eb 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -38,7 +38,7 @@ namespace Mohawk {
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;
+ _isRunning = _continueRunning = false;
}
RivenScript::~RivenScript() {
@@ -227,7 +227,7 @@ void RivenScript::dumpCommands(Common::StringArray varNames, Common::StringArray
}
void RivenScript::runScript() {
- _isRunning = true;
+ _isRunning = _continueRunning = true;
if (_stream->pos() != 0)
_stream->seek(0);
@@ -242,7 +242,7 @@ void RivenScript::processCommands(bool runCommands) {
uint16 commandCount = _stream->readUint16BE();
- for (uint16 j = 0; j < commandCount && !_vm->shouldQuit() && _stream->pos() < _stream->size(); j++) {
+ for (uint16 j = 0; j < commandCount && !_vm->shouldQuit() && _stream->pos() < _stream->size() && _continueRunning; j++) {
uint16 command = _stream->readUint16BE();
if (command == 8) {
@@ -346,9 +346,14 @@ void RivenScript::playScriptSLST(uint16 op, uint16 argc, uint16 *argv) {
_vm->_activatedSLST = true;
}
-// Command 4: play local tWAV resource (twav_id, volume, u1)
+// Command 4: play local tWAV resource (twav_id, volume, block)
void RivenScript::playSound(uint16 op, uint16 argc, uint16 *argv) {
- _vm->_sound->playSound(argv[0], false);
+ byte volume = Sound::convertRivenVolume(argv[1]);
+
+ if (argv[2] == 1)
+ _vm->_sound->playSoundBlocking(argv[0], volume);
+ else
+ _vm->_sound->playSound(argv[0], volume);
}
// Command 7: set variable value (variable, value)
@@ -398,7 +403,7 @@ void RivenScript::changeCursor(uint16 op, uint16 argc, uint16 *argv) {
void RivenScript::delay(uint16 op, uint16 argc, uint16 *argv) {
debug(2, "Delay %dms", argv[0]);
if (argv[0] > 0)
- _vm->_system->delayMillis(argv[0]);
+ _vm->delayAndUpdate(argv[0]);
}
// Command 17: call external command
@@ -572,7 +577,8 @@ void RivenScript::activateFLST(uint16 op, uint16 argc, uint16 *argv) {
for (uint16 i = 0; i < recordCount; i++) {
uint16 index = flst->readUint16BE();
uint16 sfxeID = flst->readUint16BE();
- if(flst->readUint16BE() != 0)
+
+ if (flst->readUint16BE() != 0)
warning("FLST u0 non-zero");
if (index == argv[0]) {
@@ -632,6 +638,11 @@ RivenScriptList RivenScriptManager::readScripts(Common::SeekableReadStream *stre
return scriptList;
}
+void RivenScriptManager::stopAllScripts() {
+ for (uint32 i = 0; i < _currentScripts.size(); i++)
+ _currentScripts[i]->stopRunning();
+}
+
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++) {
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index 5187bbde08..a85cde1702 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -62,6 +62,7 @@ public:
uint16 getParentStack() { return _parentStack; }
uint16 getParentCard() { return _parentCard; }
bool isRunning() { return _isRunning; }
+ void stopRunning() { _continueRunning = false; }
static uint32 calculateScriptSize(Common::SeekableReadStream *script);
@@ -76,8 +77,8 @@ private:
MohawkEngine_Riven *_vm;
Common::SeekableReadStream *_stream;
- uint16 _scriptType, _parentStack, _parentCard, _parentHotspot;
- bool _isRunning;
+ uint16 _scriptType, _parentStack, _parentCard;
+ bool _isRunning, _continueRunning;
void dumpCommands(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
void processCommands(bool runCommands);
@@ -131,6 +132,7 @@ public:
~RivenScriptManager();
RivenScriptList readScripts(Common::SeekableReadStream *stream, bool garbageCollect = true);
+ void stopAllScripts();
private:
void unloadUnusedScripts();
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index b6d2dff315..ae06afef01 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -271,7 +271,7 @@ static const char *variableNames[] = {
};
uint32 *MohawkEngine_Riven::getLocalVar(uint32 index) {
- return matchVarToString(getName(VariableNames, index));
+ return getVar(getName(VariableNames, index));
}
uint32 MohawkEngine_Riven::getGlobalVar(uint32 index) {
@@ -279,18 +279,15 @@ uint32 MohawkEngine_Riven::getGlobalVar(uint32 index) {
}
Common::String MohawkEngine_Riven::getGlobalVarName(uint32 index) {
- return Common::String(variableNames[index]);
+ return variableNames[index];
}
-uint32 *MohawkEngine_Riven::matchVarToString(Common::String varName) {
- return matchVarToString(varName.c_str());
-}
-
-uint32 *MohawkEngine_Riven::matchVarToString(const char *varName) {
+uint32 *MohawkEngine_Riven::getVar(const Common::String &varName) {
for (uint32 i = 0; i < _varCount; i++)
- if (!scumm_stricmp(varName, variableNames[i]))
+ if (varName.equalsIgnoreCase(variableNames[i]))
return &_vars[i];
- error ("Unknown variable: \'%s\'", varName);
+
+ error ("Unknown variable: '%s'", varName.c_str());
return NULL;
}
@@ -304,33 +301,33 @@ void MohawkEngine_Riven::initVars() {
_vars[i] = 0;
// Init Variables to their correct starting state.
- *matchVarToString("ttelescope") = 5;
- *matchVarToString("tgatestate") = 1;
- *matchVarToString("jbridge1") = 1;
- *matchVarToString("jbridge4") = 1;
- *matchVarToString("jgallows") = 1;
- *matchVarToString("jiconcorrectorder") = 12068577;
- *matchVarToString("bblrvalve") = 1;
- *matchVarToString("bblrwtr") = 1;
- *matchVarToString("bfans") = 1;
- *matchVarToString("bytrap") = 2;
- *matchVarToString("aatruspage") = 1;
- *matchVarToString("acathpage") = 1;
- *matchVarToString("bheat") = 1;
- *matchVarToString("waterenabled") = 1;
- *matchVarToString("ogehnpage") = 1;
- *matchVarToString("bblrsw") = 1;
- *matchVarToString("ocage") = 1;
+ *getVar("ttelescope") = 5;
+ *getVar("tgatestate") = 1;
+ *getVar("jbridge1") = 1;
+ *getVar("jbridge4") = 1;
+ *getVar("jgallows") = 1;
+ *getVar("jiconcorrectorder") = 12068577;
+ *getVar("bblrvalve") = 1;
+ *getVar("bblrwtr") = 1;
+ *getVar("bfans") = 1;
+ *getVar("bytrap") = 2;
+ *getVar("aatruspage") = 1;
+ *getVar("acathpage") = 1;
+ *getVar("bheat") = 1;
+ *getVar("waterenabled") = 1;
+ *getVar("ogehnpage") = 1;
+ *getVar("bblrsw") = 1;
+ *getVar("ocage") = 1;
// Randomize the telescope combination
- uint32 *teleCombo = matchVarToString("tcorrectorder");
+ uint32 *teleCombo = getVar("tcorrectorder");
for (byte i = 0; i < 5; i++) {
*teleCombo *= 10;
*teleCombo += _rnd->getRandomNumberRng(1, 5); // 5 buttons
}
// Randomize the prison combination
- uint32 *prisonCombo = matchVarToString("pcorrectorder");
+ uint32 *prisonCombo = getVar("pcorrectorder");
for (byte i = 0; i < 5; i++) {
*prisonCombo *= 10;
*prisonCombo += _rnd->getRandomNumberRng(1, 3); // 3 buttons/sounds
@@ -338,7 +335,7 @@ void MohawkEngine_Riven::initVars() {
// Randomize the dome combination -- each bit represents a slider position,
// the highest bit (1 << 24) represents 1, (1 << 23) represents 2, etc.
- uint32 *domeCombo = matchVarToString("adomecombo");
+ uint32 *domeCombo = getVar("adomecombo");
for (byte bitsSet = 0; bitsSet < 5;) {
uint32 randomBit = 1 << (24 - _rnd->getRandomNumber(24));
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 091bd68021..4a8c923c01 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -36,7 +36,6 @@
namespace Mohawk {
Sound::Sound(MohawkEngine* vm) : _vm(vm) {
- _rivenSoundFile = NULL;
_midiDriver = NULL;
_midiParser = NULL;
@@ -51,7 +50,6 @@ Sound::Sound(MohawkEngine* vm) : _vm(vm) {
Sound::~Sound() {
stopSound();
stopAllSLST();
- delete _rivenSoundFile;
if (_midiDriver) {
_midiDriver->close();
@@ -64,15 +62,6 @@ Sound::~Sound() {
}
}
-void Sound::loadRivenSounds(uint16 stack) {
- static const char prefixes[] = { 'a', 'b', 'g', 'j', 'o', 'p', 'r', 't' };
-
- if (!_rivenSoundFile)
- _rivenSoundFile = new MohawkArchive();
-
- _rivenSoundFile->open(Common::String(prefixes[stack]) + "_Sounds.mhk");
-}
-
void Sound::initMidi() {
if (!(_vm->getFeatures() & GF_HASMIDI))
return;
@@ -87,7 +76,7 @@ void Sound::initMidi() {
_midiParser->setTimerRate(_midiDriver->getBaseTempo());
}
-Audio::SoundHandle *Sound::playSound(uint16 id, bool mainSoundFile, byte volume, bool loop) {
+Audio::SoundHandle *Sound::playSound(uint16 id, byte volume, bool loop) {
debug (0, "Playing sound %d", id);
SndHandle *handle = getHandle();
@@ -113,21 +102,9 @@ Audio::SoundHandle *Sound::playSound(uint16 id, bool mainSoundFile, byte volume,
} else
audStream = makeMohawkWaveStream(_vm->getRawData(ID_MSND, id));
break;
- case GType_RIVEN:
- if (mainSoundFile)
- audStream = makeMohawkWaveStream(_rivenSoundFile->getRawData(ID_TWAV, id));
- else
- audStream = makeMohawkWaveStream(_vm->getRawData(ID_TWAV, id));
- break;
case GType_ZOOMBINI:
audStream = makeMohawkWaveStream(_vm->getRawData(ID_SND, id));
break;
- case GType_CSAMTRAK:
- if (mainSoundFile)
- audStream = makeMohawkWaveStream(_vm->getRawData(ID_TWAV, id));
- else
- audStream = getCSAmtrakMusic(id);
- break;
case GType_LIVINGBOOKSV1:
audStream = makeOldMohawkWaveStream(_vm->getRawData(ID_WAV, id));
break;
@@ -147,6 +124,13 @@ Audio::SoundHandle *Sound::playSound(uint16 id, bool mainSoundFile, byte volume,
return NULL;
}
+void Sound::playSoundBlocking(uint16 id, byte volume) {
+ Audio::SoundHandle *handle = playSound(id, volume);
+
+ while (_vm->_mixer->isSoundHandleActive(*handle))
+ _vm->_system->delayMillis(10);
+}
+
void Sound::playMidi(uint16 id) {
uint32 idTag;
if (!(_vm->getFeatures() & GF_HASMIDI)) {
@@ -188,6 +172,10 @@ void Sound::playMidi(uint16 id) {
_midiDriver->setTimerCallback(_midiParser, MidiParser::timerCallback);
}
+byte Sound::convertRivenVolume(uint16 volume) {
+ return (volume == 256) ? 255 : volume;
+}
+
void Sound::playSLST(uint16 index, uint16 card) {
Common::SeekableReadStream *slstStream = _vm->getRawData(ID_SLST, card);
SLSTRecord slstRecord;
@@ -304,19 +292,15 @@ void Sound::playSLSTSound(uint16 id, bool fade, bool loop, uint16 volume, int16
sndHandle.id = id;
_currentSLSTSounds.push_back(sndHandle);
- Audio::AudioStream *audStream = makeMohawkWaveStream(_rivenSoundFile->getRawData(ID_TWAV, id));
+ Audio::AudioStream *audStream = makeMohawkWaveStream(_vm->getRawData(ID_TWAV, id));
// Loop here if necessary
if (loop)
audStream = Audio::makeLoopingAudioStream((Audio::RewindableAudioStream *)audStream, 0);
- // The max mixer volume is 255 and the max Riven volume is 256. Just change it to 255.
- if (volume == 256)
- volume = 255;
-
// TODO: Handle fading, possibly just raise the volume of the channel in increments?
- _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, sndHandle.handle, audStream, -1, volume, convertBalance(balance));
+ _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, sndHandle.handle, audStream, -1, convertRivenVolume(volume), convertBalance(balance));
}
void Sound::stopSLSTSound(uint16 index, bool fade) {
@@ -336,16 +320,6 @@ void Sound::resumeSLST() {
_vm->_mixer->pauseHandle(*_currentSLSTSounds[i].handle, false);
}
-Audio::AudioStream *Sound::getCSAmtrakMusic(uint16 id) {
- char filename[18];
- sprintf(filename, "MUSIC/MUSIC%02d.MHK", id);
- MohawkArchive *file = new MohawkArchive();
- file->open(filename);
- Audio::AudioStream *audStream = makeMohawkWaveStream(file->getRawData(ID_TWAV, 2000 + id));
- delete file;
- return audStream;
-}
-
Audio::AudioStream *Sound::makeMohawkWaveStream(Common::SeekableReadStream *stream) {
uint32 tag = 0;
ADPC_Chunk adpc;
diff --git a/engines/mohawk/sound.h b/engines/mohawk/sound.h
index 0e3ecd3c51..f493130d35 100644
--- a/engines/mohawk/sound.h
+++ b/engines/mohawk/sound.h
@@ -115,28 +115,29 @@ class MohawkEngine;
class Sound {
public:
- Sound(MohawkEngine*);
+ Sound(MohawkEngine *vm);
~Sound();
- void loadRivenSounds(uint16 stack);
- Audio::SoundHandle *playSound(uint16 id, bool mainSoundFile = true, byte volume = Audio::Mixer::kMaxChannelVolume, bool loop = false);
+ Audio::SoundHandle *playSound(uint16 id, byte volume = Audio::Mixer::kMaxChannelVolume, bool loop = false);
+ void playSoundBlocking(uint16 id, byte volume = Audio::Mixer::kMaxChannelVolume);
void playMidi(uint16 id);
void stopSound();
void pauseSound();
void resumeSound();
+
+ // Riven-specific
void playSLST(uint16 index, uint16 card);
void playSLST(SLSTRecord slstRecord);
void pauseSLST();
void resumeSLST();
void stopAllSLST();
+ static byte convertRivenVolume(uint16 volume);
private:
MohawkEngine *_vm;
- MohawkArchive *_rivenSoundFile;
MidiDriver *_midiDriver;
MidiParser *_midiParser;
- static Audio::AudioStream *getCSAmtrakMusic(uint16 id);
static Audio::AudioStream *makeMohawkWaveStream(Common::SeekableReadStream *stream);
static Audio::AudioStream *makeOldMohawkWaveStream(Common::SeekableReadStream *stream);
void initMidi();
@@ -144,7 +145,7 @@ private:
Common::Array<SndHandle> _handles;
SndHandle *getHandle();
- // Riven specific
+ // Riven-specific
void playSLSTSound(uint16 index, bool fade, bool loop, uint16 volume, int16 balance);
void stopSLSTSound(uint16 id, bool fade);
Common::Array<SLSTSndHandle> _currentSLSTSounds;
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index 17456b8ec3..b7ee4c8a2c 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -123,7 +123,7 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
delete _videoStreams[videoHandle].video;
_videoStreams[videoHandle].video = 0;
- _videoStreams[videoHandle].id = 0;
+ _videoStreams[videoHandle].id = 0xffff;
_videoStreams[videoHandle].filename.clear();
}
@@ -156,7 +156,7 @@ bool VideoManager::updateBackgroundMovies() {
} else {
delete _videoStreams[i].video;
_videoStreams[i].video = 0;
- _videoStreams[i].id = 0;
+ _videoStreams[i].id = 0xffff;
_videoStreams[i].filename.clear();
continue;
}
@@ -292,7 +292,7 @@ void VideoManager::stopMovie(uint16 id) {
if (_mlstRecords[i].movieID == _videoStreams[j].id) {
delete _videoStreams[j].video;
_videoStreams[j].video = 0;
- _videoStreams[j].id = 0;
+ _videoStreams[j].id = 0xffff;
_videoStreams[j].filename.clear();
return;
}
@@ -368,7 +368,7 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u
entry.x = x;
entry.y = y;
entry.filename = filename;
- entry.id = 0;
+ entry.id = 0xffff;
entry.loop = loop;
entry.enabled = true;
@@ -412,4 +412,14 @@ uint32 VideoManager::getFrameCount(const VideoHandle &handle) {
return _videoStreams[handle]->getFrameCount();
}
+uint32 VideoManager::getElapsedTime(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->getElapsedTime();
+}
+
+bool VideoManager::endOfVideo(const VideoHandle &handle) {
+ assert(handle != NULL_VID_HANDLE);
+ return _videoStreams[handle]->endOfVideo();
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 6aa553e26b..4c6ed05cef 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -94,6 +94,8 @@ public:
VideoHandle findVideoHandle(uint16 id);
int32 getCurFrame(const VideoHandle &handle);
uint32 getFrameCount(const VideoHandle &handle);
+ uint32 getElapsedTime(const VideoHandle &handle);
+ bool endOfVideo(const VideoHandle &handle);
private:
MohawkEngine *_vm;
diff --git a/engines/parallaction/balloons.cpp b/engines/parallaction/balloons.cpp
index 95f85f6cff..1992b8dbc0 100644
--- a/engines/parallaction/balloons.cpp
+++ b/engines/parallaction/balloons.cpp
@@ -248,7 +248,7 @@ class BalloonManager_ns : public BalloonManager {
Parallaction_ns *_vm;
static int16 _dialogueBalloonX[5];
- byte _textColors[2];
+ byte _textColors[3];
struct Balloon {
Common::Rect outerBox;
@@ -530,7 +530,7 @@ public:
class BalloonManager_br : public BalloonManager {
Parallaction_br *_vm;
- byte _textColors[2];
+ byte _textColors[3];
struct Balloon {
Common::Rect box;
diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp
index 12acf7ba60..34b04cd00f 100644
--- a/engines/parallaction/disk_br.cpp
+++ b/engines/parallaction/disk_br.cpp
@@ -191,7 +191,7 @@ GfxObj* DosDisk_br::loadTalk(const char *name) {
Script* DosDisk_br::loadLocation(const char *name) {
debugC(5, kDebugDisk, "DosDisk_br::loadLocation");
- static const Common::String langs[4] = { "it/", "fr/", "en/", "ge/" };
+ static const char * const langs[4] = { "it/", "fr/", "en/", "ge/" };
Common::String fullName(name);
if (!fullName.hasSuffix(".slf")) {
diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp
index 562c806958..9f50236360 100644
--- a/engines/parallaction/gui_ns.cpp
+++ b/engines/parallaction/gui_ns.cpp
@@ -586,7 +586,7 @@ public:
if (_points[2] >= _points[0] && _points[2] >= _points[1]) {
character = CHAR_DOUGH;
} else {
- error("If you read this, either your CPU or transivity is broken (we believe the former).");
+ error("If you read this, either your CPU or transivity is broken (we believe the former)");
}
_vm->cleanupGame();
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index ce7525345a..2de7fe9d64 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -201,7 +201,7 @@ void Parallaction::allocateLocationSlot(const char *name) {
}
if (_di == 120)
- error("No more location slots available. Please report this immediately to ScummVM team.");
+ error("No more location slots available. Please report this immediately to ScummVM team");
if (_currentLocationIndex == -1) {
strcpy(_locationNames[_numLocations], name);
diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp
index 407dd86ec3..f1def31f9f 100644
--- a/engines/parallaction/sound_br.cpp
+++ b/engines/parallaction/sound_br.cpp
@@ -130,7 +130,7 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) {
break;
default:
- warning("Unexpected midi event 0x%02X in midi data.", info.event);
+ warning("Unexpected midi event 0x%02X in midi data", info.event);
}
//if ((type == 0xB) && (info.basic.param1 == 64)) info.basic.param2 = 127;
@@ -173,7 +173,7 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) {
byte *pos = data;
if (memcmp("MSCt", pos, 4)) {
- warning("Expected header not found in music file.");
+ warning("Expected header not found in music file");
return false;
}
pos += 4;
diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp
index d6df23d415..884d1a32ac 100644
--- a/engines/parallaction/walk.cpp
+++ b/engines/parallaction/walk.cpp
@@ -88,7 +88,7 @@ void PathWalker_NS::correctPathPoint(Common::Point &to) {
} while ((top > 0) && !IS_PATH_CLEAR(to.x, top));
do {
bottom++;
- } while ((bottom < maxY) && !IS_PATH_CLEAR(to.x, bottom) );
+ } while ((bottom < maxY) && !IS_PATH_CLEAR(to.x, bottom));
top = (top == 0) ? 1000 : to.y - top;
bottom = (bottom == maxY) ? 1000 : bottom - to.y;
diff --git a/engines/queen/cutaway.cpp b/engines/queen/cutaway.cpp
index 6b08dd8d63..11a8704d60 100644
--- a/engines/queen/cutaway.cpp
+++ b/engines/queen/cutaway.cpp
@@ -283,7 +283,7 @@ void Cutaway::limitBob(CutawayObject &object) {
}
BobSlot *bob =
- _vm->graphics()->bob( _vm->logic()->findBob(object.objectNumber) );
+ _vm->graphics()->bob(_vm->logic()->findBob(object.objectNumber));
if (!bob) {
warning("Failed to find bob");
@@ -667,7 +667,7 @@ const byte *Cutaway::handleAnimation(const byte *ptr, CutawayObject &object) {
// Only flip if we are not moving or it is not a person object
if (!(objAnim[i].object > 0 && objAnim[i].object < 4) ||
- !(objAnim[i].mx || objAnim[i].my) )
+ !(objAnim[i].mx || objAnim[i].my))
bob->xflip = objAnim[i].flip;
// Add frame alteration
@@ -1235,7 +1235,7 @@ void Cutaway::handleText(
}
BobSlot *bob =
- _vm->graphics()->bob( _vm->logic()->findBob(ABS(object.objectNumber)) );
+ _vm->graphics()->bob(_vm->logic()->findBob(ABS(object.objectNumber)));
_vm->graphics()->setBobText(bob, sentence, x, object.bobStartY, object.specialMove, flags);
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
index 3d859c8335..be6b0dc773 100644
--- a/engines/queen/music.cpp
+++ b/engines/queen/music.cpp
@@ -82,6 +82,11 @@ MidiMusic::MidiMusic(QueenEngine *vm)
_driver->open();
_driver->setTimerCallback(this, &timerCallback);
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
_parser = MidiParser::createParser_SMF();
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp
index 27b1e9c60a..6bc79daa73 100644
--- a/engines/queen/talk.cpp
+++ b/engines/queen/talk.cpp
@@ -551,11 +551,11 @@ bool Talk::speak(const char *sentence, Person *person, const char *voiceFilePref
return personWalking;
}
- if (0 == strcmp(person->name, "FAYE-H" ) ||
+ if (0 == strcmp(person->name, "FAYE-H") ||
0 == strcmp(person->name, "FRANK-H") ||
0 == strcmp(person->name, "AZURA-H") ||
0 == strcmp(person->name, "X3_RITA") ||
- (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FAYE_HEAD ) ||
+ (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FAYE_HEAD) ||
(0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == AZURA_HEAD) ||
(0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FRANK_HEAD))
_talkHead = true;
diff --git a/engines/saga/actor.h b/engines/saga/actor.h
index 57d06e9e3a..2f8fdea8ec 100644
--- a/engines/saga/actor.h
+++ b/engines/saga/actor.h
@@ -451,8 +451,8 @@ public:
void cmdActorWalkTo(int argc, const char **argv);
bool validActorId(uint16 id) { return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actorsCount))); }
- int actorIdToIndex(uint16 id) { return (id == ID_PROTAG ) ? 0 : objectIdToIndex(id); }
- uint16 actorIndexToId(int index) { return (index == 0 ) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); }
+ int actorIdToIndex(uint16 id) { return (id == ID_PROTAG) ? 0 : objectIdToIndex(id); }
+ uint16 actorIndexToId(int index) { return (index == 0) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); }
ActorData *getActor(uint16 actorId);
ActorData *getFirstActor() { return _actors[0]; }
diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index 5b7b7289eb..47f1a122c0 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -123,7 +123,7 @@ void Font::loadFont(uint32 fontResourceId) {
}
if (readS.pos() != FONT_DESCSIZE) {
- error("Invalid font resource size.");
+ error("Invalid font resource size");
}
font->normal.font = (byte*)malloc(fontResourceLength - FONT_DESCSIZE);
@@ -610,7 +610,7 @@ void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rec
}
w_total = 0;
len_total = 0;
- if (wc == 0) {
+ if (wc == 0 && measurePointer) {
searchPointer = measurePointer + 1;
}
wc = 0;
diff --git a/engines/saga/font.h b/engines/saga/font.h
index d8b1da30b9..1b9f290a1b 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -186,7 +186,7 @@ class Font {
void validate(FontId fontId) {
if (!valid(fontId)) {
- error("Font::validate: Invalid font id.");
+ error("Font::validate: Invalid font id");
}
}
bool valid(FontId fontId) {
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index c4b4688785..a77ec1c140 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -1398,7 +1398,7 @@ void Interface::setSave(PanelButton *panelButton) {
char *fileName;
switch (panelButton->id) {
case kTextSave:
- if (_textInputStringLength == 0 ) {
+ if (_textInputStringLength == 0) {
break;
}
if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) {
@@ -2166,43 +2166,43 @@ void Interface::drawButtonBox(const Rect& rect, ButtonKind kind, bool down) {
byte solidColor;
byte odl, our, idl, iur;
- switch (kind ) {
- case kSlider:
- cornerColor = 0x8b;
- frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
- fillColor = kITEColorLightBlue96;
- odl = kITEColorDarkBlue8a;
- our = kITEColorLightBlue92;
- idl = 0x89;
- iur = 0x94;
- solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96;
- break;
- case kEdit:
- if (_vm->getGameId() == GID_ITE) {
- cornerColor = frameColor = fillColor = kITEColorLightBlue96;
- our = kITEColorDarkBlue8a;
- odl = kITEColorLightBlue94;
- solidColor = down ? kITEColorBlue : kITEColorDarkGrey0C;
- } else {
- cornerColor = frameColor = fillColor = _vm->KnownColor2ColorId(kKnownColorBlack);
- our = odl = solidColor = _vm->KnownColor2ColorId(kKnownColorBlack);
- }
- iur = 0x97;
- idl = 0x95;
- break;
- default:
- cornerColor = 0x8b;
- frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
- solidColor = fillColor = kITEColorLightBlue96;
- odl = kITEColorDarkBlue8a;
- our = kITEColorLightBlue94;
- idl = 0x97;
- iur = 0x95;
- if (down) {
- SWAP(odl, our);
- SWAP(idl, iur);
- }
- break;
+ switch (kind) {
+ case kSlider:
+ cornerColor = 0x8b;
+ frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
+ fillColor = kITEColorLightBlue96;
+ odl = kITEColorDarkBlue8a;
+ our = kITEColorLightBlue92;
+ idl = 0x89;
+ iur = 0x94;
+ solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96;
+ break;
+ case kEdit:
+ if (_vm->getGameId() == GID_ITE) {
+ cornerColor = frameColor = fillColor = kITEColorLightBlue96;
+ our = kITEColorDarkBlue8a;
+ odl = kITEColorLightBlue94;
+ solidColor = down ? kITEColorBlue : kITEColorDarkGrey0C;
+ } else {
+ cornerColor = frameColor = fillColor = _vm->KnownColor2ColorId(kKnownColorBlack);
+ our = odl = solidColor = _vm->KnownColor2ColorId(kKnownColorBlack);
+ }
+ iur = 0x97;
+ idl = 0x95;
+ break;
+ default:
+ cornerColor = 0x8b;
+ frameColor = _vm->KnownColor2ColorId(kKnownColorBlack);
+ solidColor = fillColor = kITEColorLightBlue96;
+ odl = kITEColorDarkBlue8a;
+ our = kITEColorLightBlue94;
+ idl = 0x97;
+ iur = 0x95;
+ if (down) {
+ SWAP(odl, our);
+ SWAP(idl, iur);
+ }
+ break;
}
int x = rect.left;
diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp
index 8999211f2a..f0ad9bbd5e 100644
--- a/engines/saga/isomap.cpp
+++ b/engines/saga/isomap.cpp
@@ -303,8 +303,8 @@ void IsoMap::adjustScroll(bool jump) {
_viewScroll.x = maxScrollPos.x;
}
} else {
- _viewScroll.y = smoothSlide( _viewScroll.y, minScrollPos.y, maxScrollPos.y );
- _viewScroll.x = smoothSlide( _viewScroll.x, minScrollPos.x, maxScrollPos.x );
+ _viewScroll.y = smoothSlide(_viewScroll.y, minScrollPos.y, maxScrollPos.y);
+ _viewScroll.x = smoothSlide(_viewScroll.x, minScrollPos.x, maxScrollPos.x);
}
if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
@@ -429,7 +429,7 @@ void IsoMap::drawTiles(const Location *location) {
workAreaWidth = _vm->getDisplayInfo().width + 128;
workAreaHeight = _vm->_scene->getHeight() + 128 + 80;
- for (u1 = u0, v1 = v0; metaTileY.y < workAreaHeight; u1--, v1-- ) {
+ for (u1 = u0, v1 = v0; metaTileY.y < workAreaHeight; u1--, v1--) {
metaTileX = metaTileY;
for (u2 = u1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
@@ -611,7 +611,7 @@ void IsoMap::drawSpritePlatform(uint16 platformIndex, const Point &point, const
for (u = SAGA_PLATFORM_W - 1,
copyLocation.u() = location.u() - ((SAGA_PLATFORM_W - 1) << 4);
u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
- u--, copyLocation.u() += 16, s.x -= 16, s.y += 8 ) {
+ u--, copyLocation.u() += 16, s.x -= 16, s.y += 8) {
if (s.x < _tileClip.right && s.y > _tileClip.top) {
tileIndex = tilePlatform->tiles[u][v];
@@ -663,7 +663,7 @@ void IsoMap::drawPlatform(uint16 platformIndex, const Point &point, int16 absU,
for (u = SAGA_PLATFORM_W - 1;
u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
- u--, s.x -= 16, s.y += 8 ) {
+ u--, s.x -= 16, s.y += 8) {
if (s.x < _tileClip.right && s.y > _tileClip.top) {
tileIndex = tilePlatform->tiles[u][v];
@@ -955,7 +955,7 @@ void IsoMap::pushPoint(int16 u, int16 v, uint16 cost, uint16 direction) {
}
}
- if (mid < _queueCount ) {
+ if (mid < _queueCount) {
memmove(tilePoint + 1, tilePoint, (_queueCount - mid) * sizeof (*tilePoint));
}
_queueCount++;
@@ -1211,7 +1211,7 @@ void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 dista
for (dir = 0; dir < 8; dir++) {
terrainMask = terraComp[dir];
- if (terrainMask & SAGA_IMPASSABLE ) {
+ if (terrainMask & SAGA_IMPASSABLE) {
continue;
}
@@ -1352,11 +1352,11 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo
continue;
}
- tile = getTile(u1, v1, _platformHeight );
+ tile = getTile(u1, v1, _platformHeight);
if (tile != NULL) {
mask = tile->terrainMask;
if (((mask != 0) && (tile->GetFGDAttr() >= kTerrBlock)) ||
- ((mask != 0xFFFF) && (tile->GetBGDAttr() >= kTerrBlock)) ) {
+ ((mask != 0xFFFF) && (tile->GetBGDAttr() >= kTerrBlock))) {
pcell->visited = 1;
}
} else {
@@ -1453,7 +1453,7 @@ void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Lo
actor->_walkStepsCount = i;
if (i) {
actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i );
+ memcpy(actor->_tileDirections, res, i);
}
}
@@ -1586,7 +1586,7 @@ void IsoMap::findTilePath(ActorData* actor, const Location &start, const Locatio
actor->_walkStepsCount = i;
if (i) {
actor->setTileDirectionsSize(i, false);
- memcpy(actor->_tileDirections, res, i );
+ memcpy(actor->_tileDirections, res, i);
}
}
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index e4a16e27da..199b0dfd8a 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -49,6 +49,7 @@ MusicDriver::MusicDriver() : _isGM(false) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
_driver = MidiDriver::createMidi(dev);
+ _driverType = MidiDriver::getMusicType(dev);
if (isMT32())
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -60,6 +61,19 @@ MusicDriver::~MusicDriver() {
delete _driver;
}
+int MusicDriver::open() {
+ int retValue = _driver->open();
+ if (retValue)
+ return retValue;
+
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
+ return 0;
+}
+
void MusicDriver::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
diff --git a/engines/saga/music.h b/engines/saga/music.h
index f3b0f177b0..470b6e18b3 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -57,7 +57,7 @@ public:
void setGM(bool isGM) { _isGM = isGM; }
//MidiDriver interface implementation
- int open() { return _driver->open(); }
+ int open();
void close() { _driver->close(); }
void send(uint32 b);
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 5b13473d77..73839eb6ea 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -459,9 +459,9 @@ void Puzzle::solicitHint() {
_vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50*1000000, this);
_vm->_interface->converseClear();
- _vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 0, 1, 0, 0 );
- _vm->_interface->converseAddText(optionsStr[_lang][kRODecline], 0, 2, 0, 0 );
- _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0, 0 );
+ _vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 0, 1, 0, 0);
+ _vm->_interface->converseAddText(optionsStr[_lang][kRODecline], 0, 2, 0, 0);
+ _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0, 0);
_vm->_interface->converseDisplayText();
break;
diff --git a/engines/saga/resource.cpp b/engines/saga/resource.cpp
index cf7474adc1..7d82aa4bda 100644
--- a/engines/saga/resource.cpp
+++ b/engines/saga/resource.cpp
@@ -165,18 +165,21 @@ bool ResourceContext::load(SagaEngine *vm, Resource *resource) {
if ((patchDescription->fileType & _fileType) != 0) {
if (patchDescription->resourceId < _table.size()) {
resourceData = &_table[patchDescription->resourceId];
- resourceData->patchData = new PatchData(patchDescription->fileName);
- if (resourceData->patchData->_patchFile->open(patchDescription->fileName)) {
- resourceData->offset = 0;
- resourceData->size = resourceData->patchData->_patchFile->size();
- // ITE uses several patch files which are loaded and then not needed
- // anymore (as they're in memory), so close them here. IHNM uses only
- // 1 patch file, which is reused, so don't close it
- if (vm->getGameId() == GID_ITE)
- resourceData->patchData->_patchFile->close();
- } else {
- delete resourceData->patchData;
- resourceData->patchData = NULL;
+ // Check if we've already found a patch for this resource. One is enough.
+ if (!resourceData->patchData) {
+ resourceData->patchData = new PatchData(patchDescription->fileName);
+ if (resourceData->patchData->_patchFile->open(patchDescription->fileName)) {
+ resourceData->offset = 0;
+ resourceData->size = resourceData->patchData->_patchFile->size();
+ // ITE uses several patch files which are loaded and then not needed
+ // anymore (as they're in memory), so close them here. IHNM uses only
+ // 1 patch file, which is reused, so don't close it
+ if (vm->getGameId() == GID_ITE)
+ resourceData->patchData->_patchFile->close();
+ } else {
+ delete resourceData->patchData;
+ resourceData->patchData = NULL;
+ }
}
}
}
@@ -222,7 +225,7 @@ bool Resource::createContexts() {
// If the Wyrmkeep credits file is found, set the Wyrmkeep version flag to true
- if (Common::File::exists("graphics/credit3n.dlt")) {
+ if (Common::File::exists("credit3n.dlt")) {
_vm->_gf_wyrmkeep = true;
}
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
index d7ee037c50..2887d79693 100644
--- a/engines/saga/scene.cpp
+++ b/engines/saga/scene.cpp
@@ -987,8 +987,8 @@ void Scene::processSceneResources() {
size_t resourceDataLength;
const byte *palPointer;
size_t i;
- SAGAResourceTypes *types;
- int typesCount;
+ SAGAResourceTypes *types = 0;
+ int typesCount = 0;
SAGAResourceTypes resType;
getResourceTypes(types, typesCount);
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
index 5fd120ac33..b0e20da48c 100644
--- a/engines/saga/script.cpp
+++ b/engines/saga/script.cpp
@@ -1315,7 +1315,7 @@ void Script::setVerb(int verb) {
// engine did it, but it appears to work.
_pointerObject = ID_NOTHING;
- setLeftButtonVerb( verb );
+ setLeftButtonVerb(verb);
showVerb();
}
@@ -1549,7 +1549,7 @@ void Script::playfieldClick(const Point& mousePoint, bool leftButton) {
}
if (_pointerObject != ID_NOTHING) {
- hitObject( leftButton );
+ hitObject(leftButton);
} else {
_pendingObject[0] = ID_NOTHING;
_pendingObject[1] = ID_NOTHING;
@@ -1618,7 +1618,7 @@ void Script::playfieldClick(const Point& mousePoint, bool leftButton) {
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
} else {
if (_pendingVerb == getVerbType(kVerbLookAt)) {
- if (objectTypeId(_pendingObject[0]) != kGameObjectActor ) {
+ if (objectTypeId(_pendingObject[0]) != kGameObjectActor) {
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
} else {
doVerb();
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
index a27608dcf5..9322918db5 100644
--- a/engines/saga/sndres.cpp
+++ b/engines/saga/sndres.cpp
@@ -225,6 +225,7 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
}
Common::SeekableReadStream& readS = *file;
+ bool uncompressedSound = false;
if (soundResourceLength >= 8) {
byte header[8];
@@ -242,7 +243,6 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
resourceType = kSoundShorten;
}
- bool uncompressedSound = false;
// If patch data exists for sound resource 4 (used in ITE intro), don't treat this sound as compressed
// Patch data for this resource is in file p2_a.iaf or p2_a.voc
if (_vm->getGameId() == GID_ITE && resourceId == 4 && context->getResourceData(resourceId)->patchData != NULL)
@@ -277,7 +277,7 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
buffer.flags &= ~Audio::FLAG_16BITS;
} else {
// Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded
- if (!scumm_stricmp(context->fileName(), "voicesd.rsc"))
+ if (!uncompressedSound && !scumm_stricmp(context->fileName(), "voicesd.rsc"))
resourceType = kSoundVOX;
}
}
@@ -331,6 +331,8 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff
if (onlyHeader)
free(data);
buffer.flags |= Audio::FLAG_UNSIGNED;
+ buffer.flags &= ~Audio::FLAG_16BITS;
+ buffer.flags &= ~Audio::FLAG_STEREO;
}
if (result) {
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 7acbe56a12..79f63fded4 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -98,7 +98,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("diskdump", WRAP_METHOD(Console, cmdDiskDump));
DCmd_Register("hexdump", WRAP_METHOD(Console, cmdHexDump));
DCmd_Register("resource_id", WRAP_METHOD(Console, cmdResourceId));
- DCmd_Register("resource_size", WRAP_METHOD(Console, cmdResourceSize));
+ DCmd_Register("resource_info", WRAP_METHOD(Console, cmdResourceInfo));
DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes));
DCmd_Register("list", WRAP_METHOD(Console, cmdList));
DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep));
@@ -178,6 +178,10 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
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_read", WRAP_METHOD(Console, cmdBreakpointRead));
+ DCmd_Register("bpr", WRAP_METHOD(Console, cmdBreakpointRead)); // alias
+ DCmd_Register("bp_write", WRAP_METHOD(Console, cmdBreakpointWrite));
+ DCmd_Register("bpw", WRAP_METHOD(Console, cmdBreakpointWrite)); // 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));
@@ -323,7 +327,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" diskdump - Dumps the specified resource to disk as a patch file\n");
DebugPrintf(" hexdump - Dumps the specified resource to standard output\n");
DebugPrintf(" resource_id - Identifies a resource number by splitting it up in resource type and resource number\n");
- DebugPrintf(" resource_size - Shows the size of a resource\n");
+ DebugPrintf(" resource_info - Shows info about a resource\n");
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");
@@ -389,7 +393,9 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf("Breakpoints:\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_method / bpx - Sets a breakpoint on the execution of a specified method/selector\n");
+ DebugPrintf(" bp_read / bpr - Sets a breakpoint on reading of a specified selector\n");
+ DebugPrintf(" bp_write / bpw - Sets a breakpoint on writing to a specified 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");
@@ -641,7 +647,7 @@ bool Console::cmdDiskDump(int argc, const char **argv) {
outFile->finalize();
outFile->close();
delete outFile;
- DebugPrintf("Resource %s.%03d has been dumped to disk\n", argv[1], resNum);
+ DebugPrintf("Resource %s.%03d (located in %s) has been dumped to disk\n", argv[1], resNum, resource->getResourceLocation().c_str());
} else {
DebugPrintf("Resource %s.%03d not found\n", argv[1], resNum);
}
@@ -718,9 +724,9 @@ bool Console::cmdRoomNumber(int argc, const char **argv) {
return true;
}
-bool Console::cmdResourceSize(int argc, const char **argv) {
+bool Console::cmdResourceInfo(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Shows the size of a resource\n");
+ DebugPrintf("Shows information about a resource\n");
DebugPrintf("Usage: %s <resource type> <resource number>\n", argv[0]);
return true;
}
@@ -734,6 +740,7 @@ bool Console::cmdResourceSize(int argc, const char **argv) {
Resource *resource = _engine->getResMan()->findResource(ResourceId(res, resNum), 0);
if (resource) {
DebugPrintf("Resource size: %d\n", resource->size);
+ DebugPrintf("Resource location: %s\n", resource->getResourceLocation().c_str());
} else {
DebugPrintf("Resource %s.%03d not found\n", argv[1], resNum);
}
@@ -1095,7 +1102,7 @@ bool Console::cmdSaveGame(int argc, const char **argv) {
} else {
out->finalize();
if (out->err()) {
- warning("Writing the savegame failed.");
+ warning("Writing the savegame failed");
}
delete out;
}
@@ -1127,7 +1134,7 @@ bool Console::cmdRestoreGame(int argc, const char **argv) {
}
bool Console::cmdRestartGame(int argc, const char **argv) {
- _engine->_gamestate->abortScriptProcessing = kAbortRestartGame;;
+ _engine->_gamestate->abortScriptProcessing = kAbortRestartGame;
return Cmd_Exit(0, 0);
}
@@ -1135,10 +1142,12 @@ bool Console::cmdRestartGame(int argc, const char **argv) {
bool Console::cmdClassTable(int argc, const char **argv) {
DebugPrintf("Available classes:\n");
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);
+ Class temp = _engine->_gamestate->_segMan->_classTable[i];
+ if (temp.reg.segment) {
+ DebugPrintf(" Class 0x%x (%s) at %04x:%04x (script 0x%x)\n", i,
+ _engine->_gamestate->_segMan->getObjectName(temp.reg),
+ PRINT_REG(temp.reg),
+ temp.script);
}
}
@@ -1195,7 +1204,6 @@ bool Console::cmdParse(int argc, const char **argv) {
return true;
}
- ResultWordList words;
char *error;
char string[1000];
@@ -1207,6 +1215,8 @@ bool Console::cmdParse(int argc, const char **argv) {
}
DebugPrintf("Parsing '%s'\n", string);
+
+ ResultWordListList words;
bool res = _engine->getVocabulary()->tokenizeString(words, string, &error);
if (res && !words.empty()) {
int syntax_fail = 0;
@@ -1215,8 +1225,13 @@ bool Console::cmdParse(int argc, const char **argv) {
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);
+ for (ResultWordListList::const_iterator i = words.begin(); i != words.end(); ++i) {
+ DebugPrintf(" ");
+ for (ResultWordList::const_iterator j = i->begin(); j != i->end(); ++j) {
+ DebugPrintf("%sType[%04x] Group[%04x]", j == i->begin() ? "" : " / ", j->_class, j->_group);
+ }
+ DebugPrintf("\n");
+ }
if (_engine->getVocabulary()->parseGNF(words, true))
syntax_fail = 1; // Building a tree failed
@@ -1243,7 +1258,6 @@ bool Console::cmdSaid(int argc, const char **argv) {
return true;
}
- ResultWordList words;
char *error;
char string[1000];
byte spec[1000];
@@ -1317,6 +1331,7 @@ bool Console::cmdSaid(int argc, const char **argv) {
_engine->getVocabulary()->debugDecipherSaidBlock(spec);
printf("\n");
+ ResultWordListList words;
bool res = _engine->getVocabulary()->tokenizeString(words, string, &error);
if (res && !words.empty()) {
int syntax_fail = 0;
@@ -1325,8 +1340,15 @@ bool Console::cmdSaid(int argc, const char **argv) {
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);
+ for (ResultWordListList::const_iterator i = words.begin(); i != words.end(); ++i) {
+ DebugPrintf(" ");
+ for (ResultWordList::const_iterator j = i->begin(); j != i->end(); ++j) {
+ DebugPrintf("%sType[%04x] Group[%04x]", j == i->begin() ? "" : " / ", j->_class, j->_group);
+ }
+ DebugPrintf("\n");
+ }
+
+
if (_engine->getVocabulary()->parseGNF(words, true))
syntax_fail = 1; // Building a tree failed
@@ -2741,9 +2763,15 @@ bool Console::cmdBreakpointList(int argc, const char **argv) {
for (; bp != end; ++bp) {
DebugPrintf(" #%i: ", i);
switch (bp->type) {
- case BREAK_SELECTOR:
+ case BREAK_SELECTOREXEC:
DebugPrintf("Execute %s\n", bp->name.c_str());
break;
+ case BREAK_SELECTORREAD:
+ DebugPrintf("Read %s\n", bp->name.c_str());
+ break;
+ case BREAK_SELECTORWRITE:
+ DebugPrintf("Write %s\n", bp->name.c_str());
+ break;
case BREAK_EXPORT:
bpdata = bp->address;
DebugPrintf("Execute script %d, export %d\n", bpdata >> 16, bpdata & 0xFFFF);
@@ -2803,7 +2831,7 @@ bool Console::cmdBreakpointDelete(int argc, const char **argv) {
bool Console::cmdBreakpointMethod(int argc, const char **argv) {
if (argc != 2) {
- DebugPrintf("Sets a breakpoint on execution/access of a specified method/selector.\n");
+ DebugPrintf("Sets a breakpoint on execution 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");
@@ -2815,12 +2843,45 @@ bool Console::cmdBreakpointMethod(int argc, const char **argv) {
Thus, we can't check whether the command argument is a valid method name.
A breakpoint set on an invalid method name will just never trigger. */
Breakpoint bp;
- bp.type = BREAK_SELECTOR;
+ bp.type = BREAK_SELECTOREXEC;
+ bp.name = argv[1];
+
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_SELECTOREXEC;
+ return true;
+}
+
+bool Console::cmdBreakpointRead(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Sets a breakpoint on reading of a specified selector.\n");
+ DebugPrintf("Usage: %s <name>\n", argv[0]);
+ DebugPrintf("Example: %s ego::view\n", argv[0]);
+ return true;
+ }
+
+ Breakpoint bp;
+ bp.type = BREAK_SELECTORREAD;
bp.name = argv[1];
_debugState._breakpoints.push_back(bp);
- _debugState._activeBreakpointTypes |= BREAK_SELECTOR;
+ _debugState._activeBreakpointTypes |= BREAK_SELECTORREAD;
+ return true;
+}
+bool Console::cmdBreakpointWrite(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Sets a breakpoint on writing of a specified selector.\n");
+ DebugPrintf("Usage: %s <name>\n", argv[0]);
+ DebugPrintf("Example: %s ego::view\n", argv[0]);
+ return true;
+ }
+
+ Breakpoint bp;
+ bp.type = BREAK_SELECTORWRITE;
+ bp.name = argv[1];
+
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_SELECTORWRITE;
return true;
}
diff --git a/engines/sci/console.h b/engines/sci/console.h
index 60599ea783..8bc2da439c 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -70,7 +70,7 @@ private:
bool cmdDiskDump(int argc, const char **argv);
bool cmdHexDump(int argc, const char **argv);
bool cmdResourceId(int argc, const char **argv);
- bool cmdResourceSize(int argc, const char **argv);
+ bool cmdResourceInfo(int argc, const char **argv);
bool cmdResourceTypes(int argc, const char **argv);
bool cmdList(int argc, const char **argv);
bool cmdHexgrep(int argc, const char **argv);
@@ -135,6 +135,8 @@ private:
bool cmdBreakpointList(int argc, const char **argv);
bool cmdBreakpointDelete(int argc, const char **argv);
bool cmdBreakpointMethod(int argc, const char **argv);
+ bool cmdBreakpointRead(int argc, const char **argv);
+ bool cmdBreakpointWrite(int argc, const char **argv);
bool cmdBreakpointKernel(int argc, const char **argv);
bool cmdBreakpointFunction(int argc, const char **argv);
// VM
diff --git a/engines/sci/debug.h b/engines/sci/debug.h
index 5cf0e38fbc..d9959f0b7f 100644
--- a/engines/sci/debug.h
+++ b/engines/sci/debug.h
@@ -37,13 +37,15 @@ enum BreakpointType {
* Break when selector is executed. data contains (char *) selector name
* (in the format Object::Method)
*/
- BREAK_SELECTOR = 1,
+ BREAK_SELECTOREXEC = 1 << 0, // break when selector gets executed
+ BREAK_SELECTORREAD = 1 << 1, // break when selector gets executed
+ BREAK_SELECTORWRITE = 1 << 2, // break when selector gets executed
/**
* Break when an exported function is called. data contains
* script_no << 16 | export_no.
*/
- BREAK_EXPORT = 2
+ BREAK_EXPORT = 1 << 3
};
struct Breakpoint {
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index e330bd5f30..a4d1edf2ed 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -55,6 +55,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"laurabow", "Laura Bow: The Colonel's Bequest"},
{"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"},
+ {"mothergoose", "Mixed-Up Mother Goose"},
{"pq2", "Police Quest II: The Vengeance"},
{"qfg1", "Quest for Glory I: So You Want to Be a Hero"},
{"sq3", "Space Quest III: The Pirates of Pestulon"},
@@ -77,6 +78,7 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"longbow", "Conquests of the Longbow: The Adventures of Robin Hood"},
{"lsl1sci", "Leisure Suit Larry in the Land of the Lounge Lizards"},
{"lsl5", "Leisure Suit Larry 5: Passionate Patti Does a Little Undercover Work"},
+ {"mothergoose256", "Mixed-Up Mother Goose"},
{"msastrochicken", "Ms. Astro Chicken"},
{"pq1sci", "Police Quest: In Pursuit of the Death Angel"},
{"pq3", "Police Quest III: The Kindred"},
@@ -94,7 +96,6 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"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"},
{"pepper", "Pepper's Adventure in Time"},
{"slater", "Slater & Charlie Go Camping"},
// === SCI2 games =========================================================
@@ -170,6 +171,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = {
{ "lsl6hires", GID_LSL6HIRES },
{ "lsl7", GID_LSL7 },
{ "mothergoose", GID_MOTHERGOOSE },
+ { "mothergoose256", GID_MOTHERGOOSE256 },
{ "mothergoosehires",GID_MOTHERGOOSEHIRES },
{ "msastrochicken", GID_MSASTROCHICKEN },
{ "pepper", GID_PEPPER },
@@ -514,6 +516,15 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
resMan->init();
// TODO: Add error handling.
+#ifndef ENABLE_SCI32
+ // Is SCI32 compiled in? If not, and this is a SCI32 game,
+ // stop here
+ if (getSciVersion() >= SCI_VERSION_2) {
+ delete resMan;
+ return (const ADGameDescription *)&s_fallbackDesc;
+ }
+#endif
+
ViewType gameViews = resMan->getViewType();
// Have we identified the game views? If not, stop here
@@ -524,15 +535,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
return 0;
}
-#ifndef ENABLE_SCI32
- // Is SCI32 compiled in? If not, and this is a SCI32 game,
- // stop here
- if (getSciVersion() >= SCI_VERSION_2) {
- delete resMan;
- return (const ADGameDescription *)&s_fallbackDesc;
- }
-#endif
-
// EGA views
if (gameViews == kViewEga && s_fallbackDesc.platform != Common::kPlatformAmiga)
s_fallbackDesc.extra = "EGA";
@@ -654,7 +656,7 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNum = atoi(file->c_str() + file->size() - 3);
- if (slotNum >= 0 && slotNum < 999) {
+ if (slotNum >= 0 && slotNum <= 99) {
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
if (in) {
SavegameMetadata meta;
@@ -721,7 +723,7 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
return SaveStateDescriptor();
}
-int SciMetaEngine::getMaximumSaveSlot() const { return 999; }
+int SciMetaEngine::getMaximumSaveSlot() const { return 99; }
void SciMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String fileName = Common::String::printf("%s.%03d", target, slot);
@@ -763,7 +765,7 @@ Common::Error SciEngine::saveGameState(int slot, const char *desc) {
} else {
out->finalize();
if (out->err()) {
- warning("Writing the savegame failed.");
+ warning("Writing the savegame failed");
return Common::kWritingFailed;
}
delete out;
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index a74c34f517..e1d03d9914 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -219,6 +219,21 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Codename: Iceman - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.022 9x5.25" (label: Int#0.000.668)
+ {"iceman", "", {
+ {"resource.map", 0, "2948e06dab4930e4c8098c24ac874db8", 6252},
+ {"resource.000", 0, "b1bccd827453d4cb834bfd5b45bef63c", 26974},
+ {"resource.001", 0, "005bd332d4b0f9d8e99d3b905223a332", 126839},
+ {"resource.002", 0, "250b859381ebf2bf8922bd99683b0cc1", 307001},
+ {"resource.003", 0, "7d7a840701d2f6eff57679bf7dced747", 318060},
+ {"resource.004", 0, "e0e72970bad9a956db13dcb63d898437", 322457},
+ {"resource.005", 0, "1f2f79e399098859c73e49ac6a3545d8", 330657},
+ {"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942},
+ {"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267811},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Codename: Iceman - English DOS 1.023 (from abevi, bug report #2612718)
{"iceman", "", {
{"resource.map", 0, "da131654de1d6f640222c092313c6ca5", 6252},
@@ -351,6 +366,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "8c767b3939add63d11274065e46aad04", 713158},
AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Conquests of the Longbow DOS 1.0 EGA (4 x 5.25" disks)
+ // Provided by ssburnout in bug report #3046802
+ {"longbow", "EGA", {
+ {"resource.map", 0, "0517ca368ec844df0cb21a05020fae01", 6021},
+ {"resource.000", 0, "36e8fda5d0b8c49e587c8a9617959f72", 934643},
+ {"resource.001", 0, "76c729e563809170e6cc8b2f3f6cf0a4", 1196133},
+ {"resource.002", 0, "8c767b3939add63d11274065e46aad04", 1152478},
+ {"resource.003", 0, "7025b87e735b1df3f0e9488a621f4333", 1171439},
+ 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", {
@@ -699,6 +724,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#endif // ENABLE_SCI32
+ // Hoyle 1 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.000.104 3x5.25" (label:INT.0.000.519)
+ {"hoyle1", "", {
+ {"resource.map", 0, "d6c37503a8f282636e1b08f7a6cf4afd", 7818},
+ {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 162805},
+ {"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 342149},
+ {"resource.003", 0, "e0dd44069a62a463fd124974b915f10d", 328925},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Hoyle 1 - English DOS (supplied by wibble92 in bug report #2644547)
// SCI interpreter version 0.000.530
{"hoyle1", "", {
@@ -717,6 +752,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Hoyle 1 - English DOS (supplied by eddydrama in bug report #3052366)
+ {"hoyle1", "", {
+ {"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386},
+ {"resource.001", 0, "e0dd44069a62463fd124974b915f10d", 518127},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
#if 0 // TODO: unknown if these files are corrupt
// Hoyle 1 - English Amiga (from www.back2roots.org)
// SCI interpreter version 0.000.519 - FIXME: some have 0.000.530, others x.yyy.zzz
@@ -737,6 +779,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Hoyle 2 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.000.011 1x3.5" (label:Int#6.21.90)
+ {"hoyle2", "", {
+ {"resource.map", 0, "db0ba08b953e9904a4960ad99cd29c20", 1356},
+ {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 216315},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Hoyle 2 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
// SCI interpreter version 0.000.685
@@ -785,8 +835,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
- // Hoyle 3 - English DOS Floppy 1.0 (supplied by abevi in bug report #2612718)
+ // Hoyle 3 - English DOS Floppy (supplied by eddydrama in bug report #3038837)
{"hoyle3", "", {
+ {"resource.map", 0, "31c9fc0977ac6e5b566c37096803d0cb", 2469},
+ {"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 12070},
+ {"resource.001", 0, "ca6a9750a2c138d8bcbba369126040e9", 348646},
+ {"resource.002", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 345811},
+ {"resource.003", 0, "97cfd72633f8f9b2a0b1d4116cf3ee81", 346116},
+ {"resource.004", 0, "2884fb91b225fabd9ca87ea231293b48", 351218},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Hoyle 3 EGA - English DOS Floppy 1.0 (supplied by abevi in bug report #2612718)
+ {"hoyle3", "EGA", {
{"resource.map", 0, "1728af1f6a85938c3522e64449e76ca1", 2205},
{"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 319905},
{"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 526438},
@@ -812,7 +873,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Hoyle 4 (Hoyle Classic Card Games) - English DOS/Win
// SCI1.1
// Supplied by abevi in bug report #3039291
- {"hoyle4", "Demo", {
+ {"hoyle4", "", {
{"resource.map", 0, "2b577c975cc8d8d43f61b6a756129fe3", 4352},
{"resource.000", 0, "43e2c15ce436aab611a462ad0603e12d", 2000132},
AD_LISTEND},
@@ -889,6 +950,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // King's Quest 1 SCI Remake - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.000.051 9x5.25" (label: INT#9.19.90)
+ {"kq1sci", "SCI Remake", {
+ {"resource.map", 0, "4dac689e98b2fa6806232fdd61e24712", 9936},
+ {"resource.001", 0, "fed9e0072ffd511d248674e60dee2099", 196027},
+ {"resource.002", 0, "fed9e0072ffd511d248674e60dee2099", 330278},
+ {"resource.003", 0, "fed9e0072ffd511d248674e60dee2099", 355008},
+ {"resource.004", 0, "fed9e0072ffd511d248674e60dee2099", 265478},
+ {"resource.005", 0, "fed9e0072ffd511d248674e60dee2099", 316854},
+ {"resource.006", 0, "fed9e0072ffd511d248674e60dee2099", 351062},
+ {"resource.007", 0, "fed9e0072ffd511d248674e60dee2099", 330472},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// King's Quest 4 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
// SCI interpreter version 0.000.685
@@ -910,6 +985,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+ // King's Quest 4 - English DOS (original boxed release, 3 1/2" disks)
+ // SCI interpreter version 0.000.247
+ {"kq4sci", "", {
+ {"resource.map", 0, "042d54434174d8f9faf926ade2ffd805", 7416},
+ {"resource.001", 0, "851a62d00972dc4002f472cc0d84e71d", 491919},
+ {"resource.002", 0, "851a62d00972dc4002f472cc0d84e71d", 678804},
+ {"resource.003", 0, "851a62d00972dc4002f472cc0d84e71d", 683145},
+ {"resource.004", 0, "851a62d00972dc4002f472cc0d84e71d", 649441},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// King's Quest 4 - English DOS (from the King's Quest Collection)
// Executable scanning reports "0.000.502"
// SCI interpreter version 0.000.502
@@ -922,6 +1008,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // King's Quest 4 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.006.003 8x5.25" (label: Int.#0.000.502)
+ {"kq4sci", "", {
+ {"resource.map", 0, "a22b66e6fa0d82460b985e9f7e562950", 9384},
+ {"resource.001", 0, "6db7de6f93c6ea62dca78abee677f8c0", 174852},
+ {"resource.002", 0, "6db7de6f93c6ea62dca78abee677f8c0", 356024},
+ {"resource.003", 0, "6db7de6f93c6ea62dca78abee677f8c0", 335716},
+ {"resource.004", 0, "6db7de6f93c6ea62dca78abee677f8c0", 312231},
+ {"resource.005", 0, "6db7de6f93c6ea62dca78abee677f8c0", 283466},
+ {"resource.006", 0, "6db7de6f93c6ea62dca78abee677f8c0", 324789},
+ {"resource.007", 0, "6db7de6f93c6ea62dca78abee677f8c0", 334441},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// King's Quest 4 - English DOS
// SCI interpreter version 0.000.274
{"kq4sci", "", {
@@ -1054,6 +1154,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // King's Quest 5 EGA 1.2M disk version (from LordHoto)
+ // VERSION file reports "0.000.055"
+ {"kq5", "EGA", {
+ {"resource.002", 0, "4d74e8094ff57cea6ee92faf63dbd0af", 1195538},
+ {"resource.003", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 1092132},
+ {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 413818},
+ {"resource.001", 0, "c1eef048fa9fe76298c2d4705ef9549f", 1162752},
+ {"resource.map", 0, "53206afb4fd73871a484e83acab80f31", 7608},
+ {"resource.004", 0, "83568edf7fde18b3eed988bc5d22ceb1", 1188053},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// King's Quest 5 EGA (supplied by omer_mor in bug report #3035421)
// VERSION file reports "0.000.062"
{"kq5", "EGA", {
@@ -1069,6 +1181,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // King's Quest V DOS 0.000.062 EGA (5 x 5.25" disks)
+ // Supplied by ssburnout in bug report #3046780
+ {"kq5", "EGA", {
+ {"resource.map", 0, "ef4fdc72ca7aef62054e8b075d7960d8", 7596},
+ {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 413648},
+ {"resource.001", 0, "c1eef048fa9fe76298c2d4705ef9549f", 1162806},
+ {"resource.002", 0, "4d74e8094ff57cea6ee92faf63dbd0af", 1194799},
+ {"resource.003", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 1092325},
+ {"resource.004", 0, "8e5c1bc4d738cf7316ff506f59d265e2", 1187803},
+ 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", "", {
@@ -1153,6 +1277,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO_NONE },
+ // King's Quest 5 - Japanese PC-98 Floppy 0.000.015 (supplied by omer_mor in bug report #3073583)
+ {"kq5", "", {
+ {"resource.map", 0, "3bca188108ec5b6ad91612483a6cbc27", 7875},
+ {"resource.000", 0, "70d6a2ec17fd49a63217992fc4347cd9", 493681},
+ {"resource.001", 0, "a504e91327a4d51ee4818eb72026dbe9", 950364},
+ {"resource.002", 0, "0750a84ece1d89d3a952e2a2b90b525c", 911833},
+ {"resource.003", 0, "6f8d552b60ec82a165619a99e19c509d", 1078032},
+ {"resource.004", 0, "e114ce8f884601c43308fb5cbbea4874", 1174129},
+ {"resource.005", 0, "349ad9438172265d00680075c5a988d0", 1019669},
+ AD_LISTEND},
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+
// 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
@@ -1536,6 +1672,26 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Larry 2 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.000.011 3x3.5" (label: Int. #0.000.343)
+ {"lsl2", "", {
+ {"resource.map", 0, "e5caa855a5be78c53a6a92157d0b9f5c", 4740},
+ {"resource.001", 0, "96033f57accfca903750413fd09193c8", 474642},
+ {"resource.002", 0, "96033f57accfca903750413fd09193c8", 407014},
+ {"resource.003", 0, "96033f57accfca903750413fd09193c8", 592834},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Larry 2 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.002.000 3x3.5" (label: INT#0.000.409)
+ {"lsl2", "", {
+ {"resource.map", 0, "2c9c3b0923e3764f5ab999bcb71c2d47", 4758},
+ {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 477625},
+ {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406935},
+ {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592533},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Larry 3 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
// SCI interpreter version 0.000.685
@@ -1561,6 +1717,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Larry 3 - English DOS (supplied by ssburnout in bug report #3049193)
+ // 1.021 8x5.25" (label: Int#5.15.90)
+ {"lsl3", "", {
+ {"resource.map", 0, "a39a20580362af3437352dbc717734f8", 7452},
+ {"resource.001", 0, "f18441027154292836b973c655fa3175", 141515},
+ {"resource.002", 0, "f18441027154292836b973c655fa3175", 345494},
+ {"resource.003", 0, "f18441027154292836b973c655fa3175", 329220},
+ {"resource.004", 0, "f18441027154292836b973c655fa3175", 290303},
+ {"resource.005", 0, "f18441027154292836b973c655fa3175", 303905},
+ {"resource.006", 0, "f18441027154292836b973c655fa3175", 282649},
+ {"resource.007", 0, "f18441027154292836b973c655fa3175", 257178},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Larry 3 - English DOS
// SCI interpreter version 0.000.572
{"lsl3", "", {
@@ -1608,6 +1778,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+ // Larry 3 1.050 Fr/En (9 x 5.25" disks)
+ // Provided by ssburnout in bug report #3046779
+ {"lsl3", "", {
+ {"resource.map", 0, "527277cee7b31dd603229443b48e70c4", 8910},
+ {"resource.001", 0, "65f1bdaa20f6d0470e9d969f22473873", 162132},
+ {"resource.002", 0, "65f1bdaa20f6d0470e9d969f22473873", 309705},
+ {"resource.003", 0, "65f1bdaa20f6d0470e9d969f22473873", 346507},
+ {"resource.004", 0, "65f1bdaa20f6d0470e9d969f22473873", 331947},
+ {"resource.005", 0, "65f1bdaa20f6d0470e9d969f22473873", 347136},
+ {"resource.006", 0, "65f1bdaa20f6d0470e9d969f22473873", 325292},
+ {"resource.007", 0, "65f1bdaa20f6d0470e9d969f22473873", 308982},
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+
// Larry 5 - English Amiga
// Executable scanning reports "1.004.023"
// SCI interpreter version 1.000.784
@@ -1717,6 +1901,32 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::IT_ITA, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Larry 5 1.0 EGA DOS (8 x 3.5" disks)
+ // Provided by ssburnout in bug report #3046806
+ {"lsl5", "EGA", {
+ {"resource.map", 0, "1370ae356fdda2e7f9ea56dda3ff9a57", 6597},
+ {"resource.000", 0, "f2537473213d70e7f4fc82e988ab90ca", 248416},
+ {"resource.001", 0, "bb642b0b0f879aca98addd62d901387e", 445841},
+ {"resource.002", 0, "c2cb2dec12e26f6243bc1b78e4e84940", 617030},
+ {"resource.003", 0, "f8e876302a3aba5bcaab5c51db6b6532", 682911},
+ {"resource.004", 0, "16f4d8fb1b526125edaca4fc6cbb7530", 530230},
+ {"resource.005", 0, "6043b2cc23d663e6a01b25bd0e4de55e", 576442},
+ {"resource.006", 0, "f6046a8445422f17d40b1b10ab21ebf3", 568551},
+ {"resource.007", 0, "640ee65595d40372ef95462f2c1ae28a", 593429},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Larry 5 EGA
+ // Supplied by omer_mor in bug report #3049771
+ {"lsl5", "EGA", {
+ {"resource.map", 0, "89dbf8006985ec0c547ffe125c25ebf9", 6255},
+ {"resource.000", 0, "f2537473213d70e7f4fc82e988ab90ca", 765747},
+ {"resource.001", 0, "bb642b0b0f879aca98addd62d901387e", 1196260},
+ {"resource.002", 0, "5a55af4e40728b1a8103dc47ad2afa8d", 1100539},
+ {"resource.003", 0, "16f4d8fb1b526125edaca4fc6cbb7530", 1064563},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Larry 6 - English DOS (from spookypeanut)
// SCI interpreter version 1.001.113
{"lsl6", "", {
@@ -1968,9 +2178,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Mixed-Up Mother Goose - English DOS Floppy EGA (supplied by ssburnout in bug report #3049193)
+ // 1.011 5x5.25" (label: Int#8.2.90)
+ {"mothergoose", "EGA", {
+ {"resource.map", 0, "7d308bfc6006d0e20985a7295c238efc", 2010},
+ {"resource.000", 0, "bb662eebeb5ffea2d705064801f6f70f", 140375},
+ {"resource.001", 0, "13ddcdf971339150c2963548c9761b31", 52648},
+ {"resource.002", 0, "13ddcdf971339150c2963548c9761b31", 204401},
+ {"resource.003", 0, "e2c858b89e89bffe37b33e01d2827930", 166990},
+ {"resource.004", 0, "dbbc22f124533ce308bc386b08956326", 146251},
+ {"resource.005", 0, "2ba5348e7fad641b9c4c7ff7c7cf4e68", 110979},
+ 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", "", {
+ {"mothergoose256", "", {
{"resource.map", 0, "52aae15e493cafd1da7e1c9b657a5bb9", 7026},
{"resource.000", 0, "b7ecd8ae9e254e80310b5a668b276e6e", 2948975},
AD_LISTEND},
@@ -1979,7 +2202,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Mixed-Up Mother Goose - English DOS CD (from jvprat)
// Executable scanning reports "x.yyy.zzz"
// SCI interpreter version 0.000.999 (just a guess)
- {"mothergoose", "CD", {
+ {"mothergoose256", "CD", {
{"resource.map", 0, "1c7f311b0a2c927b2fbe81ae341fb2f6", 5790},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 4369438},
AD_LISTEND},
@@ -1987,7 +2210,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Mixed-Up Mother Goose - English Windows Interactive Demo
// Executable scanning reports "x.yyy.zzz"
- {"mothergoose", "Demo", {
+ {"mothergoose256", "Demo", {
{"resource.map", 0, "87f9dc1cafc4d4fa835fb2f00cf3a6ef", 4560},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 2070072},
AD_LISTEND},
@@ -2249,6 +2472,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO_NOSPEECH },
+ // Police Quest 3 EGA
+ // Reported by musiclyinspired in bug report #3046573
+ {"pq3", "", {
+ {"resource.map", 0, "1341f7c9643947414a8e238b88f68d82", 5901},
+ {"resource.000", 0, "7659713720d61d9465a59091b7ee63ea", 402208},
+ {"resource.001", 0, "0284ca44341fbc3cb7a047e49d230234", 703373},
+ {"resource.002", 0, "fc9452f962bd7a9bbf6e78e9e52a8e18", 692676},
+ {"resource.003", 0, "31c226bf01b69c8182b8ca0e8760b0a7", 527848},
+ {"resource.004", 0, "b96a86ab681769e4cbb439670d967ca6", 449682},
+ {"resource.005", 0, "9e6c53a0e7eef53694d260fade8b1fc7", 724000},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Police Quest 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.096
{"pq4", "Demo", {
@@ -2341,6 +2577,36 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by ssburnout in bug report #3049193)
+ // 1.001 10x5.25" (label: INT.#0.000.566)
+ {"qfg1", "", {
+ {"resource.map", 0, "c5a0346ff16c43b1eea9583d15e7743c", 6948},
+ {"resource.000", 0, "481b034132106390cb5160fe61dd5f58", 80334},
+ {"resource.001", 0, "4d67acf52833ff45c7f753d6663532e8", 95500},
+ {"resource.002", 0, "3e2a89d60d385caca5b3394049da4bc4", 271587},
+ {"resource.003", 0, "e56e9fd2f7d2c98774699f7a5087e524", 256373},
+ {"resource.004", 0, "d74cd4290bf60e1409117202e4ce8592", 266415},
+ {"resource.005", 0, "7288ed6d5da89b7a80b4af3897a7963a", 271185},
+ {"resource.006", 0, "69366c2a2f99917199fe1b60a4fee19d", 267852},
+ {"resource.007", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 272747},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by ssburnout in bug report #3049193)
+ // 1.200 10x5.25" (label: INT#9.10.90)
+ {"qfg1", "", {
+ {"resource.map", 0, "96939838dd9aa17b110c25256f04dd0b", 6906},
+ {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 79181},
+ {"resource.001", 0, "917fcef303e9489597154727baaa9e07", 74752},
+ {"resource.002", 0, "c000304092dc439d5103563853b4fc6d", 273186},
+ {"resource.003", 0, "1903eb08c02e2218b4a38ab9d5553e01", 258115},
+ {"resource.004", 0, "4b8e46d72ce887d13c552be56db3b3c8", 267882},
+ {"resource.005", 0, "f40198349d542e105d040743435e0cd6", 268907},
+ {"resource.006", 0, "f46690dca714abc8c89357d30e363dd3", 278387},
+ {"resource.007", 0, "951299a82a8134ed12c5c18118d45c2f", 269173},
+ 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", {
@@ -2446,6 +2712,33 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO_NOSPEECH },
+ // Quest for Glory 2 - English (supplied by ssburnout in bug report #3049193)
+ // 1.000 5x5.25" (label: INT#10.31.90)
+ {"qfg2", "", {
+ {"resource.map", 0, "5b07fa7ea23afb7dd6804e64e7f7470f", 6906},
+ {"resource.000", 0, "a17e374c4d33b81208c862bc0ffc1a38", 212151},
+ {"resource.001", 0, "e4cc56e7a471325bc8ba1dc78334f52f", 866944},
+ {"resource.002", 0, "5f08242f962293be8fb852f183342350", 790850},
+ {"resource.003", 0, "0790f67d87642132be515cab05026baa", 972144},
+ {"resource.004", 0, "2ac1e6fea9aa1f5b91a06693a67b9766", 982830},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Quest for Glory 2 - English (supplied by ssburnout in bug report #3049193)
+ // 1.000 9x3.5" (label: INT#10.31.90)
+ {"qfg2", "", {
+ {"resource.map", 0, "1e30119a632a53eb8343fff7c9989025", 8148},
+ {"resource.000", 0, "a17e374c4d33b81208c862bc0ffc1a38", 212151},
+ {"resource.001", 0, "e4cc56e7a471325bc8ba1dc78334f52f", 331803},
+ {"resource.002", 0, "5f08242f962293be8fb852f183342350", 468129},
+ {"resource.003", 0, "5f08242f962293be8fb852f183342350", 501963},
+ {"resource.004", 0, "5f08242f962293be8fb852f183342350", 482486},
+ {"resource.005", 0, "5f08242f962293be8fb852f183342350", 478071},
+ {"resource.006", 0, "5e9deacbdb17198ad844988e04833520", 498593},
+ {"resource.007", 0, "2ac1e6fea9aa1f5b91a06693a67b9766", 490151},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Quest for Glory 2 - English (from FRG)
// Executable scanning reports "1.000.072"
{"qfg2", "", {
@@ -2522,6 +2815,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NONE },
+ // Quest for Glory 3 - Italian DOS
+ // Supplied by ghoost in bug report #3053457
+ {"qfg3", "", {
+ {"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958},
+ {"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868000},
+ {"resource.msg", 0, "5a0a896ff3e4a628db38a75eb6c84114", 259018},
+ AD_LISTEND},
+ Common::IT_ITA, 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", {
@@ -2725,6 +3027,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::ES_ESP, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Space Quest I 2.0 EGA DOS (6 x 3.5" disks)
+ // Provided by ssburnout in bug report #3046805
+ {"sq1sci", "EGA Remake", {
+ {"resource.map", 0, "dc1bb935bf32da652b2e687617f50cd4", 6003},
+ {"resource.000", 0, "e9d866534f8c84de82e25f2631ff258c", 409145},
+ {"resource.001", 0, "a89b7b52064c75b1985b289edc2f5c69", 647747},
+ {"resource.002", 0, "f43d4f08547336c9fd28c23a7da79c41", 697438},
+ {"resource.003", 0, "4164edf21495b9114f9a514e401b4d95", 669070},
+ {"resource.004", 0, "975c6e81194ae6b65e960a248129ecaa", 684119},
+ {"resource.005", 0, "13d96f7905637552c0647175ff816145", 695589},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Space Quest 3 - English Amiga (from www.back2roots.org)
// SCI interpreter version 0.000.453 (just a guess)
{"sq3", "", {
@@ -2851,7 +3166,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO_NOSPEECH },
-#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)
@@ -2859,8 +3173,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a18088c8aceb06025dbc945f29e02935", 5124},
{"resource.000", 0, "e1f46832cd2458796028e054a0466031", 5502009},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
-#endif
+ Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, GUIO_NOSPEECH },
// Space Quest 4 - English DOS
// Executable scanning reports "1.000.753"
@@ -2897,6 +3210,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Space Quest IV DOS 1.060 EGA (6 x 3.5" disks)
+ // Supplied by ssburnout in bug report #3046781
+ {"sq4", "EGA", {
+ {"resource.map", 0, "4f59814d23a3721f251140fdcfebe35d", 5556},
+ {"resource.000", 0, "e1f46832cd2458796028e054a0466031", 385479},
+ {"resource.001", 0, "590b996f85333dba50cfdd1489de2be2", 617504},
+ {"resource.002", 0, "ea8c49b84c6e641e7600cbca90a81741", 632814},
+ {"resource.003", 0, "33c396eb78bafaec38480bcdd9024843", 627369},
+ {"resource.004", 0, "9a673e33c3f6dd560b993ffed77eeb49", 534994},
+ {"resource.005", 0, "3c4841d0a3ebba4404af588c93620c22", 595465},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// Space Quest 4 - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
{"sq4", "", {
@@ -3016,7 +3342,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
-#if 0
// Space Quest 5 - English DOS - THIS IS THE UNOFFICIAL BETA VERSION, WHICH IS OBVIOUSLY PIRATED AND CONTAINS MANY BUGS
// ffs. http://www.akril15.com/sr/sq5alt/sq5alt.html =DO NOT RE-ADD=
// SCI interpreter version 1.001.067
@@ -3024,8 +3349,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8bde0a9adb9a3e9aaa861826874c9834", 6473},
{"resource.000", 0, "f4a48705764544d7cc64a7bb22a610df", 6025184},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
-#endif
+ Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, 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
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index f99d412c64..97eec38caf 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -138,9 +138,9 @@ 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)
- _doSoundType = SCI_VERSION_0_EARLY;
+ // Almost all of the SCI0EARLY games use different sound resources than
+ // SCI0LATE. Although the last SCI0EARLY game (lsl2) uses SCI0LATE resources
+ _doSoundType = g_sci->getResMan()->detectEarlySound() ? SCI_VERSION_0_EARLY : SCI_VERSION_0_LATE;
#ifdef ENABLE_SCI32
} else if (getSciVersion() >= SCI_VERSION_2_1) {
_doSoundType = SCI_VERSION_2_1;
@@ -275,15 +275,8 @@ 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
+ // Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss
+ reg_t gameSuperClass = g_sci->getGameSuperClassAddress();
bool found = false;
if (!gameSuperClass.isNull()) {
Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass);
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index 936b83d760..c1939c6566 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -28,6 +28,8 @@
namespace Sci {
+//#define GC_DEBUG_CODE
+
struct WorklistManager {
Common::Array<reg_t> _worklist;
AddrSet _map;
@@ -153,10 +155,12 @@ void run_gc(EngineState *s) {
// Some debug stuff
debugC(2, kDebugLevelGC, "[GC] Running...");
+#ifdef GC_DEBUG_CODE
const char *segnames[SEG_TYPE_MAX + 1];
int segcount[SEG_TYPE_MAX + 1];
memset(segnames, 0, sizeof(segnames));
memset(segcount, 0, sizeof(segcount));
+#endif
// Compute the set of all segments references currently in use.
AddrSet *activeRefs = findAllActiveReferences(s);
@@ -166,10 +170,13 @@ void run_gc(EngineState *s) {
const Common::Array<SegmentObj *> &heap = segMan->getSegments();
for (uint seg = 1; seg < heap.size(); seg++) {
SegmentObj *mobj = heap[seg];
+
if (mobj != NULL) {
+#ifdef GC_DEBUG_CODE
const SegmentType type = mobj->getType();
segnames[type] = SegmentObj::getSegmentTypeName(type);
-
+#endif
+
// 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);
@@ -179,7 +186,9 @@ void run_gc(EngineState *s) {
// Not found -> we can free it
mobj->freeAtAddress(segMan, addr);
debugC(2, kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
+#ifdef GC_DEBUG_CODE
segcount[type]++;
+#endif
}
}
@@ -188,11 +197,13 @@ void run_gc(EngineState *s) {
delete activeRefs;
+#ifdef GC_DEBUG_CODE
// 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]);
+#endif
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 157884fac3..ff327a0049 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -744,11 +744,9 @@ 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) {
- // hoyle 3 has cantBeHere selector but is assuming to call kCanBeHere
- if (g_sci->getGameId() != GID_HOYLE3)
- _kernelNames[0x4d] = "CantBeHere";
- }
+ // If vocab.999 exists, the kernel function is still named CanBeHere
+ if (_selectorCache.cantBeHere != -1)
+ _kernelNames[0x4d] = "CantBeHere";
switch (getSciVersion()) {
case SCI_VERSION_0_EARLY:
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index b6247b46f1..e50c6aaae2 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -278,7 +278,6 @@ private:
/******************** Kernel functions ********************/
-// New kernel functions
reg_t kStrLen(EngineState *s, int argc, reg_t *argv);
reg_t kGetFarText(EngineState *s, int argc, reg_t *argv);
reg_t kReadNumber(EngineState *s, int argc, reg_t *argv);
@@ -419,6 +418,8 @@ 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 kShow(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors(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);
@@ -443,13 +444,14 @@ reg_t kAddPlane(EngineState *s, int argc, reg_t *argv);
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv);
reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv);
reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv);
+reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv);
reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv);
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv);
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); // kOnMe for SCI2, kIsOnMe for SCI2.1
reg_t kListIndexOf(EngineState *s, int argc, reg_t *argv);
reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv);
reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv);
reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv);
-reg_t kOnMe(EngineState *s, int argc, reg_t *argv);
reg_t kInPolygon(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
@@ -458,13 +460,15 @@ 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);
+reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv);
+reg_t kWinHelp(EngineState *s, int argc, reg_t *argv);
+reg_t kWinDLL(EngineState *s, int argc, reg_t *argv);
+reg_t kPrintDebug(EngineState *s, int argc, reg_t *argv);
#endif
reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index e71e97e95d..d2c95053d5 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -81,6 +81,8 @@ struct SciKernelMapSubEntry {
#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL
#define MAP_CALL(_name_) #_name_, k##_name_
+#define MAP_EMPTY(_name_) #_name_, kEmpty
+#define MAP_DUMMY(_name_) #_name_, kDummy
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kDoSound_subops[] = {
@@ -248,7 +250,7 @@ static const SciKernelMapSubEntry kFileIO_subops[] = {
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, 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 },
@@ -257,20 +259,14 @@ static const SciKernelMapSubEntry kList_subops[] = {
{ 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, 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, 18, MAP_CALL(ListIndexOf) , "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 },
@@ -323,13 +319,13 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, kDisplay_workarounds },
// ^ 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(DirLoop), SIG_EVERYWHERE, "oi", NULL, kDirLoop_workarounds },
{ 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(DoAvoider), SIG_EVERYWHERE, "o(i)", 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
@@ -409,12 +405,13 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", 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(RestoreGame), SIG_EVERYWHERE, "[r0]i[r0]", NULL, NULL },
{ MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL },
+ { MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](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
@@ -430,11 +427,12 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(Show), SIG_EVERYWHERE, "i", 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(StrCat), SIG_EVERYWHERE, "rr", NULL, kStrCat_workarounds },
{ 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 },
@@ -454,8 +452,25 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+ // Unimplemented SCI0-SCI1.1 unused functions, always mapped to kDummy
+ { MAP_DUMMY(InspectObj), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowSends), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowObjs), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShowFree), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(StackUsage), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(Profiler), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ShiftScreen), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ListOps), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(ATan), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(Record), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(PlayBack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(DbugStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
+ // =======================================================================================================
+
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
+ // TODO: whoever knows his way through those calls, fix the signatures.
{ MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@@ -471,15 +486,22 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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 },
+ { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL },
{ MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii(i)", 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 empty functions
+
+ // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable
+ // memory") are available. We have our own memory manager and garbage collector, thus we ignore this call.
+ { MAP_EMPTY(Purge), SIG_EVERYWHERE, "i", NULL, NULL },
+
// SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", 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 },
@@ -487,8 +509,32 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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 }
+ { MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(WinDLL), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(PrintDebug), SIG_EVERYWHERE, "ri", NULL, NULL },
+
+ // SCI2.1 empty functions
+
+ // SetWindowsOption is used to set Windows specific options, like for example the title bar visibility of
+ // the game window in Phantasmagoria 2. We ignore these settings completely.
+ { MAP_EMPTY(SetWindowsOption), SIG_EVERYWHERE, "ii", NULL, NULL },
+
+ // Unimplemented SCI2.1 unused functions, always mapped to kDummy
+ { MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Profiler (same as SCI0-SCI1.1)
+ // Record (same as SCI0-SCI1.1)
+ // PlayBack (same as SCI0-SCI1.1)
+ { MAP_DUMMY(MonoOut), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(SetFatalStr), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(IntegrityChecking),SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(CheckIntegrity), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(MarkMemory), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(GetSierraProfileString), SIG_EVERYWHERE, "(.*)", NULL, NULL },
#endif
+
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
};
/** Default kernel name table. */
@@ -502,7 +548,7 @@ static const char *s_defaultKernelNames[] = {
/*0x06*/ "IsObject",
/*0x07*/ "RespondsTo",
/*0x08*/ "DrawPic",
- /*0x09*/ "Dummy", // Show
+ /*0x09*/ "Show",
/*0x0a*/ "PicNotValid",
/*0x0b*/ "Animate",
/*0x0c*/ "SetNowSeen",
@@ -574,20 +620,20 @@ static const char *s_defaultKernelNames[] = {
/*0x4a*/ "ReadNumber",
/*0x4b*/ "BaseSetter",
/*0x4c*/ "DirLoop",
- /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
+ /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
/*0x4e*/ "OnControl",
/*0x4f*/ "InitBresen",
/*0x50*/ "DoBresen",
- /*0x51*/ "Platform", // DoAvoider (SCI0)
+ /*0x51*/ "Platform", // DoAvoider (SCI0)
/*0x52*/ "SetJump",
- /*0x53*/ "SetDebug",
- /*0x54*/ "Dummy", // InspectObj
- /*0x55*/ "Dummy", // ShowSends
- /*0x56*/ "Dummy", // ShowObjs
- /*0x57*/ "Dummy", // ShowFree
+ /*0x53*/ "SetDebug", // for debugging
+ /*0x54*/ "InspectObj", // for debugging
+ /*0x55*/ "ShowSends", // for debugging
+ /*0x56*/ "ShowObjs", // for debugging
+ /*0x57*/ "ShowFree", // for debugging
/*0x58*/ "MemoryInfo",
- /*0x59*/ "Dummy", // StackUsage
- /*0x5a*/ "Dummy", // Profiler
+ /*0x59*/ "StackUsage", // for debugging
+ /*0x5a*/ "Profiler", // for debugging
/*0x5b*/ "GetMenu",
/*0x5c*/ "SetMenu",
/*0x5d*/ "GetSaveFiles",
@@ -608,33 +654,33 @@ static const char *s_defaultKernelNames[] = {
/*0x6c*/ "Graph",
/*0x6d*/ "Joystick",
// End of kernel function table for SCI0
- /*0x6e*/ "Dummy", // ShiftScreen
+ /*0x6e*/ "ShiftScreen", // never called?
/*0x6f*/ "Palette",
/*0x70*/ "MemorySegment",
/*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
/*0x72*/ "Memory",
- /*0x73*/ "Dummy", // ListOps
+ /*0x73*/ "ListOps", // never called?
/*0x74*/ "FileIO",
/*0x75*/ "DoAudio",
/*0x76*/ "DoSync",
/*0x77*/ "AvoidPath",
- /*0x78*/ "Sort", // StrSplit (SCI01)
- /*0x79*/ "Dummy", // ATan
+ /*0x78*/ "Sort", // StrSplit (SCI01)
+ /*0x79*/ "ATan", // never called?
/*0x7a*/ "Lock",
/*0x7b*/ "StrSplit",
- /*0x7c*/ "GetMessage", // Message (SCI1.1)
+ /*0x7c*/ "GetMessage", // Message (SCI1.1)
/*0x7d*/ "IsItSkip",
/*0x7e*/ "MergePoly",
/*0x7f*/ "ResCheck",
/*0x80*/ "AssertPalette",
/*0x81*/ "TextColors",
/*0x82*/ "TextFonts",
- /*0x83*/ "Dummy", // Record
- /*0x84*/ "Dummy", // PlayBack
+ /*0x83*/ "Record", // for debugging
+ /*0x84*/ "PlayBack", // for debugging
/*0x85*/ "ShowMovie",
/*0x86*/ "SetVideoMode",
/*0x87*/ "SetQuitStr",
- /*0x88*/ "Dummy" // DbugStr
+ /*0x88*/ "DbugStr" // for debugging
};
#ifdef ENABLE_SCI32
@@ -757,13 +803,13 @@ static const char *sci2_default_knames[] = {
/*0x70*/ "InPolygon",
/*0x71*/ "MergePoly",
/*0x72*/ "SetDebug",
- /*0x73*/ "InspectObject",
+ /*0x73*/ "InspectObject", // for debugging
/*0x74*/ "MemoryInfo",
- /*0x75*/ "Profiler",
- /*0x76*/ "Record",
- /*0x77*/ "PlayBack",
- /*0x78*/ "MonoOut",
- /*0x79*/ "SetFatalStr",
+ /*0x75*/ "Profiler", // for debugging
+ /*0x76*/ "Record", // for debugging
+ /*0x77*/ "PlayBack", // for debugging
+ /*0x78*/ "MonoOut", // for debugging
+ /*0x79*/ "SetFatalStr", // for debugging
/*0x7a*/ "GetCWD",
/*0x7b*/ "ValidPath",
/*0x7c*/ "FileIO",
@@ -775,10 +821,10 @@ static const char *sci2_default_knames[] = {
/*0x82*/ "Array",
/*0x83*/ "String",
/*0x84*/ "RemapColors",
- /*0x85*/ "IntegrityChecking",
- /*0x86*/ "CheckIntegrity",
+ /*0x85*/ "IntegrityChecking", // for debugging
+ /*0x86*/ "CheckIntegrity", // for debugging
/*0x87*/ "ObjectIntersect",
- /*0x88*/ "MarkMemory",
+ /*0x88*/ "MarkMemory", // for debugging
/*0x89*/ "TextWidth",
/*0x8a*/ "PointSize",
@@ -934,7 +980,7 @@ static const char *sci21_default_knames[] = {
/*0x7c*/ "SetQuitStr",
/*0x7d*/ "GetConfig",
/*0x7e*/ "Table",
- /*0x7f*/ "WinHelp", // Windows only
+ /*0x7f*/ "WinHelp", // Windows only
/*0x80*/ "Dummy",
/*0x81*/ "Dummy",
/*0x82*/ "Dummy",
@@ -956,8 +1002,8 @@ static const char *sci21_default_knames[] = {
/*0x92*/ "PlayVMD",
/*0x93*/ "SetHotRectangles",
/*0x94*/ "MulDiv",
- /*0x95*/ "GetSierraProfileInt", // Windows only
- /*0x96*/ "GetSierraProfileString", // Windows only
+ /*0x95*/ "GetSierraProfileInt", // , Windows only
+ /*0x96*/ "GetSierraProfileString", // , Windows only
/*0x97*/ "SetWindowsOption", // Windows only
/*0x98*/ "GetWindowsOption", // Windows only
/*0x99*/ "WinDLL", // Windows only
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index 3395811700..9d48174078 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -23,6 +23,8 @@
*
*/
+#include "common/system.h"
+
#include "sci/sci.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
@@ -42,7 +44,6 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
int mask = argv[0].toUint16();
reg_t obj = argv[1];
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;
@@ -67,13 +68,24 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
}
- oldx = mousePos.x;
- oldy = mousePos.y;
curEvent = g_sci->getEventManager()->getSciEvent(mask);
if (g_sci->getVocabulary())
g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
+ if (s->_cursorWorkaroundActive) {
+ // ffs: GfxCursor::setPosition()
+ // we check, if actual cursor position is inside given rect
+ // if that's the case, we switch ourself off. Otherwise
+ // we simulate the original set position to the scripts
+ if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) {
+ s->_cursorWorkaroundActive = false;
+ } else {
+ mousePos.x = s->_cursorWorkaroundPoint.x;
+ mousePos.y = s->_cursorWorkaroundPoint.y;
+ }
+ }
+
writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x);
writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y);
@@ -162,6 +174,21 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
g_sci->_soundCmd->updateSci0Cues();
}
+ // Wait a bit here, so that the CPU isn't maxed out when the game
+ // is waiting for user input (e.g. when showing text boxes) - bug
+ // #3037874. This works when games do benchmarking at the beginning,
+ // because most of them call kAnimate for benchmarking without
+ // calling kGetEvent in between (rightly so).
+ if (g_sci->getGameId() == GID_JONES && g_sci->getEngineState()->currentRoomNumber() == 764) {
+ // Jones CD is an exception, as it incorrectly calls GetEvent
+ // while benchmarking. Thus, don't delay here for room 764 in
+ // Jones (the speed test room), otherwise speed testing will
+ // fail and the game won't show any views, as it will think that
+ // the user has a slow machine - bug #3058865
+ } else {
+ g_system->delayMillis(10);
+ }
+
return s->r_acc;
}
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 39c32ccc68..b6d67513d2 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -24,9 +24,13 @@
*/
#include "common/archive.h"
+#include "common/config-manager.h"
#include "common/file.h"
#include "common/str.h"
#include "common/savefile.h"
+#include "common/translation.h"
+
+#include "gui/saveload.h"
#include "sci/sci.h"
#include "sci/engine/state.h"
@@ -37,7 +41,7 @@
namespace Sci {
struct SavegameDesc {
- uint id;
+ int16 id;
int virtualId; // straight numbered, according to id but w/o gaps
int date;
int time;
@@ -98,13 +102,9 @@ enum {
-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;
-
+reg_t file_open(EngineState *s, const char *filename, int mode, bool unwrapFilename) {
Common::String englishName = g_sci->getSciLanguageString(filename, K_LANG_ENGLISH);
- const Common::String wrappedName = g_sci->wrapFilename(englishName);
+ Common::String wrappedName = unwrapFilename ? g_sci->wrapFilename(englishName) : englishName;
Common::SeekableReadStream *inFile = 0;
Common::WriteStream *outFile = 0;
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
@@ -180,7 +180,7 @@ reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
int mode = argv[1].toUint16();
debugC(2, kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode);
- return file_open(s, name.c_str(), mode);
+ return file_open(s, name.c_str(), mode, true);
}
static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
@@ -218,29 +218,31 @@ reg_t kFPuts(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
+static int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
FileHandle *f = getFileFromHandle(s, handle);
if (!f)
- return;
+ return 0;
if (!f->_in) {
error("fgets_wrapper: Trying to read from file '%s' opened for writing", f->_name.c_str());
- return;
+ return 0;
}
+ int readBytes = 0;
if (maxsize > 1) {
memset(dest, 0, maxsize);
f->_in->readLine(dest, maxsize);
+ readBytes = strlen(dest); // FIXME: sierra sci returned byte count and didn't react on NUL characters
// The returned string must not have an ending LF
- int strSize = strlen(dest);
- if (strSize > 0) {
- if (dest[strSize - 1] == 0x0A)
- dest[strSize - 1] = 0;
+ if (readBytes > 0) {
+ if (dest[readBytes - 1] == 0x0A)
+ dest[readBytes - 1] = 0;
}
} else {
- *dest = f->_in->readByte();
+ *dest = 0;
}
debugC(2, kDebugLevelFile, " -> FGets'ed \"%s\"", dest);
+ return readBytes;
}
reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
@@ -249,9 +251,9 @@ reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
int handle = argv[2].toUint16();
debugC(2, kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize);
- fgets_wrapper(s, buf, maxsize, handle);
+ int readBytes = fgets_wrapper(s, buf, maxsize, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
- return argv[0];
+ return readBytes ? argv[0] : NULL_REG;
}
/**
@@ -269,7 +271,7 @@ reg_t kGetCWD(EngineState *s, int argc, reg_t *argv) {
}
static void listSavegames(Common::Array<SavegameDesc> &saves);
-static int findSavegame(Common::Array<SavegameDesc> &saves, uint saveId);
+static int findSavegame(Common::Array<SavegameDesc> &saves, int16 saveId);
enum {
K_DEVICE_INFO_GET_DEVICE = 0,
@@ -452,7 +454,7 @@ static void listSavegames(Common::Array<SavegameDesc> &saves) {
}
// Find a savedgame according to virtualId and return the position within our array
-static int findSavegame(Common::Array<SavegameDesc> &saves, uint savegameId) {
+static int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId) {
for (uint saveNr = 0; saveNr < saves.size(); saveNr++) {
if (saves[saveNr].id == savegameId)
return saveNr;
@@ -460,7 +462,7 @@ static int findSavegame(Common::Array<SavegameDesc> &saves, uint savegameId) {
return -1;
}
-// The scripts get IDs ranging from 1000->1999, because the scripts require us to assign unique ids THAT EVEN STAY BETWEEN
+// The scripts get IDs ranging from 100->199, 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
@@ -494,7 +496,7 @@ reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
// Find saved-game
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
- error("kCheckSaveGame: called with invalid savegameId!");
+ error("kCheckSaveGame: called with invalid savegameId");
uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
int savegameNr = findSavegame(saves, savegameId);
if (savegameNr == -1)
@@ -548,78 +550,110 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
- Common::String game_id = s->_segMan->getString(argv[0]);
- uint virtualId = argv[1].toUint16();
- Common::String game_description = s->_segMan->getString(argv[2]);
+ Common::String game_id;
+ int16 virtualId = argv[1].toSint16();
+ int16 savegameId = -1;
+ Common::String game_description;
Common::String version;
+
if (argc > 3)
version = s->_segMan->getString(argv[3]);
- 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);
-
- 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)
+ if (argv[0].isNull()) {
+ // Direct call, from a patched Game::save
+ if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
+ error("kSaveGame: assumed patched call isn't accurate");
+
+ // we are supposed to show a dialog for the user and let him choose where to save
+ g_sci->_soundCmd->pauseAll(true); // pause music
+ const EnginePlugin *plugin = NULL;
+ EngineMan.findGame(g_sci->getGameIdStr(), &plugin);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"));
+ dialog->setSaveMode(true);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ game_description = dialog->getResultString();
+ if (game_description.empty()) {
+ // create our own description for the saved game, the user didnt enter it
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
+ curTime.tm_year += 1900; // fixup year
+ curTime.tm_mon++; // fixup month
+ game_description = Common::String::printf("%02d.%02d.%04d / %02d:%02d:%02d", curTime.tm_mday, curTime.tm_mon, curTime.tm_year, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ }
+ delete dialog;
+ g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
+ if (savegameId < 0)
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)
+
+ } else {
+ // Real call from script
+ game_id = s->_segMan->getString(argv[0]);
+ if (argv[2].isNull())
+ error("kSaveGame: called with description being NULL");
+ game_description = s->_segMan->getString(argv[2]);
+
+ debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ 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 (savegameNr == saves.size())
- break;
+ if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
+ error("kSavegame: no more savegame slots available");
}
- if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
- error("kSavegame: no more savegame slots available");
+ } else {
+ error("kSaveGame: invalid savegameId used");
}
- } else {
- error("kSaveGame: invalid savegameId used");
+
+ // Save in case caller wants to overwrite last newly created save
+ s->_lastSaveVirtualId = virtualId;
+ s->_lastSaveNewId = savegameId;
}
- // Save in case caller wants to overwrite last newly created save
- s->_lastSaveVirtualId = virtualId;
- s->_lastSaveNewId = savegameId;
+ s->r_acc = NULL_REG;
Common::String filename = g_sci->getSavegameName(savegameId);
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
Common::OutSaveFile *out;
if (!(out = saveFileMan->openForSaving(filename))) {
warning("Error opening savegame \"%s\" for writing", filename.c_str());
- s->r_acc = NULL_REG;
- return NULL_REG;
- }
-
- if (!gamestate_save(s, out, game_description.c_str(), version.c_str())) {
- warning("Saving the game failed.");
- s->r_acc = NULL_REG;
} else {
- out->finalize();
- if (out->err()) {
- delete out;
- warning("Writing the savegame failed.");
- s->r_acc = NULL_REG;
+ if (!gamestate_save(s, out, game_description.c_str(), version.c_str())) {
+ warning("Saving the game failed");
} else {
+ out->finalize();
+ if (out->err()) {
+ warning("Writing the savegame failed");
+ } else {
+ s->r_acc = TRUE_REG; // success
+ }
delete out;
- s->r_acc = make_reg(0, 1);
}
}
@@ -628,41 +662,75 @@ 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]) : "";
- uint savegameId = argv[1].toUint16();
+ int16 savegameId = argv[1].toSint16();
+ bool pausedMusic = false;
debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
if (argv[0].isNull()) {
- // Loading from the launcher, don't adjust the ID of the saved game
+ // Direct call, either from launcher or from a patched Game::restore
+ if (savegameId == -1) {
+ // we are supposed to show a dialog for the user and let him choose a saved game
+ g_sci->_soundCmd->pauseAll(true); // pause music
+ const EnginePlugin *plugin = NULL;
+ EngineMan.findGame(g_sci->getGameIdStr(), &plugin);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"));
+ dialog->setSaveMode(false);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ delete dialog;
+ if (savegameId < 0) {
+ g_sci->_soundCmd->pauseAll(false); // unpause music
+ return s->r_acc;
+ }
+ pausedMusic = true;
+ }
+ // don't adjust ID of the saved game, it's already correct
} else {
- if ((savegameId < 1000) || (savegameId > 1999)) {
+ if (argv[2].isNull())
+ error("kRestoreGame: called with parameter 2 being NULL");
+ // Real call from script, we need to adjust ID
+ if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
warning("Savegame ID %d is not allowed", savegameId);
return TRUE_REG;
}
- savegameId -= 1000;
+ savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
}
+ s->r_acc = NULL_REG; // signals success
+
Common::Array<SavegameDesc> saves;
listSavegames(saves);
if (findSavegame(saves, savegameId) == -1) {
+ s->r_acc = TRUE_REG;
warning("Savegame ID %d not found", savegameId);
- return TRUE_REG;
- }
-
- 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
+ } else {
+ 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
- gamestate_restore(s, in);
- delete in;
+ gamestate_restore(s, in);
+ delete in;
- return s->r_acc;
+ if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
+ // WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
+ // saving a previously restored game.
+ // We set the current savedgame-id directly and remove the script
+ // code concerning this via script patch.
+ s->variables[VAR_GLOBAL][0xB3].offset = SAVEGAMEID_OFFICIALRANGE_START + savegameId;
+ }
+ } else {
+ s->r_acc = TRUE_REG;
+ warning("Savegame #%d not found", savegameId);
+ }
}
- s->r_acc = TRUE_REG;
- warning("Savegame #%d not found", savegameId);
+ if (!s->r_acc.isNull()) {
+ // no success?
+ if (pausedMusic)
+ g_sci->_soundCmd->pauseAll(false); // unpause music
+ }
return s->r_acc;
}
@@ -676,45 +744,6 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
}
-reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
- // Verify that we are given a valid buffer
- if (!buffer.segment) {
- error("DirSeeker::firstFile('%s') invoked with invalid buffer", mask.c_str());
- return NULL_REG;
- }
- _outbuffer = buffer;
-
- // Prefix the mask
- const Common::String wrappedMask = g_sci->wrapFilename(mask);
-
- // Obtain a list of all savefiles matching the given mask
- 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.
- _iter = _savefiles.begin();
- return nextFile(segMan);
-}
-
-reg_t DirSeeker::nextFile(SegManager *segMan) {
- if (_iter == _savefiles.end()) {
- return NULL_REG;
- }
-
- const Common::String wrappedString = *_iter;
-
- // Strip the prefix
- Common::String string = g_sci->unwrapFilename(wrappedString);
- if (string.size() > 12)
- string = Common::String(string.c_str(), 12);
- segMan->strcpy(_outbuffer, string.c_str());
-
- // Return the result and advance the list iterator :)
- ++_iter;
- return _outbuffer;
-}
-
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
if (!s)
return make_reg(0, getSciVersion());
@@ -727,6 +756,7 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
// 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();
+ bool unwrapFilename = true;
// SQ4 floppy prepends /\ to the filenames
if (name.hasPrefix("/\\")) {
@@ -744,11 +774,21 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
}
if (name.empty()) {
- warning("Attempted to open a file with an empty filename");
+ // Happens many times during KQ1 (e.g. when typing something)
+ debugC(2, kDebugLevelFile, "Attempted to open a file with an empty filename");
return SIGNAL_REG;
}
debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
- return file_open(s, name.c_str(), mode);
+
+ // QFG import rooms get a virtual filelisting instead of an actual one
+ if (g_sci->inQfGImportRoom()) {
+ // we need to find out what the user actually selected, "savedHeroes" is already destroyed
+ // when we get here. That's why we need to remember selection via kDrawControl
+ name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem);
+ unwrapFilename = false;
+ }
+
+ return file_open(s, name.c_str(), mode, unwrapFilename);
}
reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
@@ -840,10 +880,10 @@ reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
int handle = argv[2].toUint16();
debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
- fgets_wrapper(s, buf, size, handle);
+ int readBytes = fgets_wrapper(s, buf, size, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, size);
delete[] buf;
- return argv[0];
+ return readBytes ? argv[0] : NULL_REG;
}
reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
@@ -852,9 +892,12 @@ reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelFile, "kFileIO(writeString): %d", handle);
FileHandle *f = getFileFromHandle(s, handle);
- if (f)
+
+ if (f) {
f->_out->write(str.c_str(), str.size());
return NULL_REG;
+ }
+
return make_reg(0, 6); // DOS - invalid handle
}
@@ -865,23 +908,117 @@ reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
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));
+
return SIGNAL_REG;
}
+void DirSeeker::addAsVirtualFiles(Common::String title, Common::String fileMask) {
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ Common::StringArray foundFiles = saveFileMan->listSavefiles(fileMask);
+ if (!foundFiles.empty()) {
+ _files.push_back(title);
+ _virtualFiles.push_back("");
+ Common::StringArray::iterator it;
+ Common::StringArray::iterator it_end = foundFiles.end();
+
+ for (it = foundFiles.begin(); it != it_end; it++) {
+ Common::String regularFilename = *it;
+ Common::String wrappedFilename = Common::String(regularFilename.c_str() + fileMask.size() - 1);
+
+ Common::SeekableReadStream *testfile = saveFileMan->openForLoading(regularFilename);
+ int32 testfileSize = testfile->size();
+ delete testfile;
+ if (testfileSize > 1024) // check, if larger than 1k. in that case its a saved game.
+ continue; // and we dont want to have those in the list
+ // We need to remove the prefix for display purposes
+ _files.push_back(wrappedFilename);
+ // but remember the actual name as well
+ _virtualFiles.push_back(regularFilename);
+ }
+ }
+}
+
+Common::String DirSeeker::getVirtualFilename(uint fileNumber) {
+ if (fileNumber >= _virtualFiles.size())
+ error("invalid virtual filename access");
+ return _virtualFiles[fileNumber];
+}
+
+reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
+ // Verify that we are given a valid buffer
+ if (!buffer.segment) {
+ error("DirSeeker::firstFile('%s') invoked with invalid buffer", mask.c_str());
+ return NULL_REG;
+ }
+ _outbuffer = buffer;
+ _files.clear();
+ _virtualFiles.clear();
+
+ int QfGImport = g_sci->inQfGImportRoom();
+ if (QfGImport) {
+ _files.clear();
+ addAsVirtualFiles("-QfG1-", "qfg1-*");
+ addAsVirtualFiles("-QfG1VGA-", "qfg1vga-*");
+ if (QfGImport > 2)
+ addAsVirtualFiles("-QfG2-", "qfg2-*");
+ if (QfGImport > 3)
+ addAsVirtualFiles("-QfG3-", "qfg3-*");
+
+ if (QfGImport == 3) {
+ // QfG3 sorts the filelisting itself, we can't let that happen otherwise our
+ // virtual list would go out-of-sync
+ reg_t savedHeros = segMan->findObjectByName("savedHeros");
+ if (!savedHeros.isNull())
+ writeSelectorValue(segMan, savedHeros, SELECTOR(sort), 0);
+ }
+
+ } else {
+ // Prefix the mask
+ const Common::String wrappedMask = g_sci->wrapFilename(mask);
+
+ // Obtain a list of all files matching the given mask
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ _files = saveFileMan->listSavefiles(wrappedMask);
+ }
+
+ // Reset the list iterator and write the first match to the output buffer,
+ // if any.
+ _iter = _files.begin();
+ return nextFile(segMan);
+}
+
+reg_t DirSeeker::nextFile(SegManager *segMan) {
+ if (_iter == _files.end()) {
+ return NULL_REG;
+ }
+
+ Common::String string;
+
+ if (_virtualFiles.empty()) {
+ // Strip the prefix, if we don't got a virtual filelisting
+ const Common::String wrappedString = *_iter;
+ string = g_sci->unwrapFilename(wrappedString);
+ } else {
+ string = *_iter;
+ }
+ if (string.size() > 12)
+ string = Common::String(string.c_str(), 12);
+ segMan->strcpy(_outbuffer, string.c_str());
+
+ // Return the result and advance the list iterator :)
+ ++_iter;
+ return _outbuffer;
+}
+
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);
- }
-
// We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
if (mask == "*.*")
mask = "*";
@@ -960,7 +1097,7 @@ 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?
+ return s->r_acc; // FIXME: does this really not return anything?
}
reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
@@ -974,7 +1111,7 @@ 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?
+ return s->r_acc; // FIXME: does this really not return anything?
}
reg_t kCD(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index a3f7a90da3..07385fd3f9 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -57,48 +57,46 @@
namespace Sci {
-void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) {
+void showScummVMDialog(const Common::String &message) {
+ GUI::MessageDialog dialog(message, "OK");
+ dialog.runModal();
+}
+
+void kDirLoopWorker(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) {
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);
if (signal & kSignalDoesntTurn)
return;
- angle %= 360;
-
- if (!oldScriptHeader) {
- if (angle < 45)
- loopNo = 3;
- else if (angle < 136)
- loopNo = 0;
- else if (angle < 225)
- loopNo = 2;
- else if (angle < 316)
- loopNo = 1;
- else
- loopNo = 3;
+ int16 useLoop = -1;
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ if ((angle > 315) || (angle < 45)) {
+ useLoop = 3;
+ } else if ((angle > 135) && (angle < 225)) {
+ useLoop = 2;
+ }
} else {
- if (angle >= 330 || angle <= 30)
- loopNo = 3;
- else if (angle <= 150)
- loopNo = 0;
- else if (angle <= 210)
- loopNo = 2;
- else if (angle < 330)
- loopNo = 1;
- else loopNo = -1;
+ // SCI0EARLY
+ if ((angle > 330) || (angle < 30)) {
+ useLoop = 3;
+ } else if ((angle > 150) && (angle < 210)) {
+ useLoop = 2;
+ }
+ }
+ if (useLoop == -1) {
+ if (angle >= 180) {
+ useLoop = 1;
+ } else {
+ useLoop = 0;
+ }
+ } else {
+ int16 loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
+ if (loopCount < 4)
+ return;
}
- maxLoops = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
-
-
- if ((loopNo > 1) && (maxLoops < 4))
- return;
-
- writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo);
+ writeSelectorValue(s->_segMan, object, SELECTOR(loop), useLoop);
}
static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) {
@@ -131,8 +129,7 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxCursor->kernelHide();
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
+ g_sci->_gfxCursor->kernelClearZoomZone();
break;
case -2:
g_sci->_gfxCursor->kernelResetMoveZone();
@@ -186,15 +183,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
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
+ g_sci->_gfxCursor->kernelSetZoomZone(argv[0].toUint16(),
+ Common::Rect(argv[1].toUint16(), argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16()),
+ argv[5].toUint16(), argv[6].toUint16(), argv[7].toUint16(),
+ argv[8].toUint16(), argv[9].toUint16());
break;
default :
error("kSetCursor: Unhandled case: %d arguments given", argc);
@@ -264,7 +256,9 @@ reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
int16 control = (argc > 6) ? argv[6].toSint16() : -1;
- // TODO: Find out why we get >15 for color in EGA
+ // TODO: Find out why we get > 15 for color in EGA
+ // FIXME: EGA? Which EGA? SCI0 or SCI1? Check the
+ // workarounds inside kGraphFillBoxAny and kNewWindow
if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
color &= 0x0F;
@@ -303,6 +297,13 @@ reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
int16 priority = argv[6].toSint16(); // yes, we may read from stack sometimes here
int16 control = argv[7].toSint16(); // sierra did the same
+ // WORKAROUND: PQ3 EGA is setting invalid colors (above 0 - 15).
+ // Colors above 15 are all white in SCI1 EGA games, which is why this was never
+ // observed. We clip them all to (0, 15) instead, as colors above 15 are used
+ // for the undithering algorithm in EGA games - bug #3048908.
+ if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY)
+ color &= 0x0F;
+
g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
return s->r_acc;
}
@@ -397,7 +398,7 @@ reg_t kPriCoord(EngineState *s, int argc, reg_t *argv) {
}
reg_t kDirLoop(EngineState *s, int argc, reg_t *argv) {
- _k_dirloop(argv[0], argv[1].toUint16(), s, argc, argv);
+ kDirLoopWorker(argv[0], argv[1].toUint16(), s, argc, argv);
return s->r_acc;
}
@@ -801,6 +802,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
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);
+ s->r_acc = g_sci->_gfxText16->allocAndFillReferenceRectArray();
return;
case SCI_CONTROLS_TYPE_TEXTEDIT:
@@ -896,6 +898,10 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
reg_t controlObject = argv[0];
Common::String objName = s->_segMan->getObjectName(controlObject);
+ // Most of the time, we won't return anything to the caller
+ // but |r| textcodes will trigger creation of rects in memory and will then set s->r_acc
+ s->r_acc = NULL_REG;
+
// Disable the "Change Directory" button, as we don't allow the game engine to
// change the directory where saved games are placed
// "changeDirItem" is used in the import windows of QFG2&3
@@ -922,20 +928,19 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
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 "
+ showScummVMDialog("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();
+ "for Quest for Glory 2. Example: 'qfg2-thief.sav'.");
}
}
+ s->_chosenQfGImportItem = readSelectorValue(s->_segMan, controlObject, SELECTOR(mark));
}
_k_GenericDrawControl(s, controlObject, false);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kHiliteControl(EngineState *s, int argc, reg_t *argv) {
@@ -1078,6 +1083,15 @@ reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
int colorPen = (argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0;
int colorBack = (argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255;
+ // WORKAROUND: PQ3 EGA is setting invalid colors (above 0 - 15).
+ // Colors above 15 are all white in SCI1 EGA games, which is why this was never
+ // observed. We clip them all to (0, 15) instead, as colors above 15 are used
+ // for the undithering algorithm in EGA games - bug #3048908.
+ if (g_sci->getResMan()->getViewType() == kViewEga && getSciVersion() >= SCI_VERSION_1_EARLY) {
+ colorPen &= 0x0F;
+ colorBack &= 0x0F;
+ }
+
// const char *title = argv[4 + argextra].segment ? kernel_dereference_char_pointer(s, argv[4 + argextra], 0) : NULL;
if (argc>=13) {
rect2 = Common::Rect (argv[5].toSint16(), argv[4].toSint16(), argv[7].toSint16(), argv[6].toSint16());
@@ -1152,6 +1166,81 @@ reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+/**
+ * Debug command, used by the SCI builtin debugger
+ */
+reg_t kShow(EngineState *s, int argc, reg_t *argv) {
+ uint16 map = argv[0].toUint16();
+
+ switch (map) {
+ case 1: // Visual, substituted by display for us
+ g_sci->_gfxScreen->debugShowMap(3);
+ break;
+ case 2: // Priority
+ g_sci->_gfxScreen->debugShowMap(1);
+ break;
+ case 3: // Control
+ case 4: // Control
+ g_sci->_gfxScreen->debugShowMap(2);
+ break;
+ default:
+ warning("Map %d is not available", map);
+ }
+
+ return s->r_acc;
+}
+
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
+ // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
+ kStub(s, argc, argv);
+
+ uint16 operation = argv[0].toUint16();
+
+ switch (operation) {
+ case 0: { // Initialize remapping to base. 0 turns remapping off.
+ //int16 unk1 = (argc >= 2) ? argv[1].toSint16() : 0;
+ }
+ break;
+ case 1: { // unknown
+ // The demo of QFG4 calls this with 1+3 parameters, thus there are differences here
+ //int16 unk1 = argv[1].toSint16();
+ //int16 unk2 = argv[2].toSint16();
+ //int16 unk3 = argv[3].toSint16();
+ //uint16 unk4 = argv[4].toUint16();
+ //uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0;
+ }
+ break;
+ case 2: { // remap by percent
+ //int16 unk1 = argv[1].toSint16();
+ //uint16 percent = argv[2].toUint16();
+ //uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
+ }
+ break;
+ case 3: { // remap to gray
+ //int16 unk1 = argv[1].toSint16();
+ //int16 percent = argv[2].toSint16(); // 0 - 100
+ //uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
+ }
+ break;
+ case 4: { // unknown
+ //int16 unk1 = argv[1].toSint16();
+ //uint16 unk2 = argv[2].toUint16();
+ //uint16 unk3 = argv[3].toUint16();
+ //uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0;
+ }
+ break;
+ case 5: { // increment color
+ //int16 unk1 = argv[1].toSint16();
+ //uint16 unk2 = argv[2].toUint16();
+ }
+ break;
+ default:
+ break;
+ }
+
+ return s->r_acc;
+}
+
#ifdef ENABLE_SCI32
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
@@ -1172,69 +1261,38 @@ reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
}
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- reg_t viewObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddScreenItem(viewObj);
- return NULL_REG;
+ g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
+ return s->r_acc;
}
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;
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
+ return s->r_acc;
}
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;
- */
-
- // TODO
-
- //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority);
- return NULL_REG;
+ g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]);
+ return s->r_acc;
}
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddPlane(planeObj);
- return NULL_REG;
+ g_sci->_gfxFrameout->kernelAddPlane(argv[0]);
+ return s->r_acc;
}
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelDeletePlane(planeObj);
- return NULL_REG;
+ g_sci->_gfxFrameout->kernelDeletePlane(argv[0]);
+ return s->r_acc;
}
reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelUpdatePlane(planeObj);
+ g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]);
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;
+ g_sci->_gfxFrameout->kernelRepaintPlane(argv[0]);
+ return s->r_acc;
}
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
@@ -1261,9 +1319,8 @@ reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
-reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
- // Tests if the cursor is on the passed object
-
+// Tests if the coordinate is on the passed object
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
uint16 x = argv[0].toUint16();
uint16 y = argv[1].toUint16();
reg_t targetObject = argv[2];
@@ -1296,75 +1353,9 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
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()) {
@@ -1421,6 +1412,61 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) {
+ uint16 windowsOption = argv[0].toUint16();
+ switch (windowsOption) {
+ case 0:
+ // Title bar on/off in Phantasmagoria, we return 0 (off)
+ return NULL_REG;
+ default:
+ warning("GetWindowsOption: Unknown option %d", windowsOption);
+ return NULL_REG;
+ }
+}
+
+reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 1:
+ // Load a help file
+ // Maybe in the future we can implement this, but for now this message should suffice
+ showScummVMDialog("Please use an external viewer to open the game's help file: " + s->_segMan->getString(argv[1]));
+ break;
+ case 2:
+ // Looks like some init function
+ break;
+ default:
+ warning("Unknown kWinHelp subop %d", argv[0].toUint16());
+ }
+
+ return s->r_acc;
+}
+
+/**
+ * Used to programmatically mass set properties of the target plane
+ */
+reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
+ // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
+ kStub(s, argc, argv);
+
+ // showStyle matches the style selector of the associated plane object
+ uint16 showStyle = argv[0].toUint16(); // 0 - 15
+ reg_t planeObj = argv[1];
+ //argv[2]
+ //int16 priority = argv[3].toSint16();
+ //argv[4]
+ //argv[5]
+ //argv[6]
+ //argv[7]
+ //int16 unk8 = (argc >= 9) ? argv[8].toSint16() : 0;
+
+ if (showStyle > 15) {
+ warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj));
+ return s->r_acc;
+ }
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 93e95099f5..2188087b8c 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -40,6 +40,9 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t 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 if ((g_sci->getGameId() == GID_HOYLE1) && (g_sci->getEngineState()->currentRoomNumber() == 3)) {
+ // HOYLE1: after sorting cards in hearts, in the next round
+ // we get an invalid node - bug #3038433
} else {
error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
}
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index 332fbb62f8..792181b832 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -35,19 +35,21 @@ reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
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);
+ // numbers are definitely unsigned, for example lsl5 door code in k rap radio is random
+ // and 5-digit - we get called kRandom(10000, 65000)
+ // some codes in sq4 are also random and 5 digit (if i remember correctly)
+ const uint16 fromNumber = argv[0].toUint16();
+ const uint16 toNumber = argv[1].toUint16();
+ uint16 range = toNumber - fromNumber + 1;
+ // calculating range is exactly how sierra sci did it and is required for hoyle 4
+ // where we get called with kRandom(0, -1) and we are supposed to give back values from 0 to 0
+ // the returned value will be used as displace-offset for a background cel
+ // note: i assume that the hoyle4 code is actually buggy and it was never fixed because of
+ // the way sierra sci handled it - "it just worked". It should have called kRandom(0, 0)
+ if (range)
+ range--; // the range value was never returned, our random generator gets 0->range, so fix it
+
+ const int randomNumber = fromNumber + (int)g_sci->getRNG().getRandomNumber(range);
return make_reg(0, randomNumber);
}
@@ -70,30 +72,24 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16())));
}
-reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) {
- // Based on behavior observed with a test program created with
- // SCI Studio.
- int x1 = argv[0].toSint16();
- int y1 = argv[1].toSint16();
- int x2 = argv[2].toSint16();
- int y2 = argv[3].toSint16();
- int xrel = x2 - x1;
- int yrel = y1 - y2; // y-axis is mirrored.
- int angle;
+uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) {
+ int16 xRel = x2 - x1;
+ int16 yRel = y1 - y2; // y-axis is mirrored.
+ int16 angle;
// Move (xrel, yrel) to first quadrant.
if (y1 < y2)
- yrel = -yrel;
+ yRel = -yRel;
if (x2 < x1)
- xrel = -xrel;
+ xRel = -xRel;
// Compute angle in grads.
- if (yrel == 0 && xrel == 0)
- angle = 0;
+ if (yRel == 0 && xRel == 0)
+ return 0;
else
- angle = 100 * xrel / (xrel + yrel);
+ angle = 100 * xRel / (xRel + yRel);
- // Fix up angle for actual quadrant of (xrel, yrel).
+ // Fix up angle for actual quadrant of (xRel, yRel).
if (y1 < y2)
angle = 200 - angle;
if (x2 < x1)
@@ -103,8 +99,18 @@ reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) {
// grad 10 with grad 11, grad 20 with grad 21, etc. This leads to
// "degrees" that equal either one or two grads.
angle -= (angle + 9) / 10;
+ return angle;
+}
+
+reg_t kGetAngle(EngineState *s, int argc, reg_t *argv) {
+ // Based on behavior observed with a test program created with
+ // SCI Studio.
+ int x1 = argv[0].toSint16();
+ int y1 = argv[1].toSint16();
+ int x2 = argv[2].toSint16();
+ int y2 = argv[3].toSint16();
- return make_reg(0, angle);
+ return make_reg(0, kGetAngleWorker(x1, y1, x2, y2));
}
reg_t kGetDistance(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp
index c8a6e03556..428c27ca73 100644
--- a/engines/sci/engine/kmenu.cpp
+++ b/engines/sci/engine/kmenu.cpp
@@ -46,12 +46,13 @@ reg_t kSetMenu(EngineState *s, int argc, reg_t *argv) {
uint16 itemId = argv[0].toUint16() & 0xFF;
uint16 attributeId;
int argPos = 1;
+ reg_t value;
while (argPos < argc) {
attributeId = argv[argPos].toUint16();
- if ((argPos + 1) >= argc)
- error("Too few parameters for kSetMenu");
- g_sci->_gfxMenu->kernelSetAttribute(menuId, itemId, attributeId, argv[argPos + 1]);
+ // Happens in the fanmade game Cascade Quest when loading - bug #3038767
+ value = (argPos + 1 < argc) ? argv[argPos + 1] : NULL_REG;
+ g_sci->_gfxMenu->kernelSetAttribute(menuId, itemId, attributeId, value);
argPos += 2;
}
return s->r_acc;
@@ -76,6 +77,11 @@ reg_t kDrawStatus(EngineState *s, int argc, reg_t *argv) {
// Sometimes this is called without giving text, if thats the case dont process it.
text = s->_segMan->getString(textReference);
+ if (text == "Replaying sound") {
+ // Happens in the fanmade game Cascade Quest when loading - ignore it
+ return s->r_acc;
+ }
+
g_sci->_gfxMenu->kernelDrawStatus(g_sci->strSplit(text.c_str(), NULL).c_str(), colorPen, colorBack);
}
return s->r_acc;
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index fbe20410de..d8ae1a3418 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -188,7 +188,9 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
int mode = (argc > 0) ? argv[0].toUint16() : 0;
- if (getSciVersion() <= SCI_VERSION_0_LATE && mode > 1)
+ // Modes 2 and 3 are supported since 0.629.
+ // This condition doesn't check that exactly, but close enough.
+ if (getSciVersion() == SCI_VERSION_0_EARLY && mode > 1)
error("kGetTime called in SCI0 with mode %d (expected 0 or 1)", mode);
switch (mode) {
@@ -387,6 +389,18 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kWinDLL(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+
+ // TODO: This seems to be loading and calling Windows DLLs. We'll probably
+ // need to either ignore calls made here, or wire each call for each game
+ // that requests it by hand
+
+ error("kWinDLL called");
+}
+#endif
+
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
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 8c43a35fea..db54705694 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -129,7 +129,7 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
// Compute x step
if (tmp != 0)
- vx = (int)(dx * sqrt(gy / (2.0 * tmp)));
+ vx = (int16)((float)(dx * sqrt(gy / (2.0 * tmp))));
else
vx = 0;
@@ -145,7 +145,7 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
// FIXME: This choice of vy makes t roughly (2+sqrt(2))/gy * sqrt(dy);
// so if gy==3, then t is roughly sqrt(dy)...
- vy = (int)sqrt((double)gy * ABS(2 * dy)) + 1;
+ vy = (int)sqrt((float)gy * ABS(2 * dy)) + 1;
} else {
// As stated above, the vertical direction is correlated to the horizontal by the
// (non-zero) factor c.
@@ -166,230 +166,345 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-#define _K_BRESEN_AXIS_X 0
-#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 = 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;
- int numsteps;
- int deltax_step;
- int deltay_step;
-
- if (numsteps_x > numsteps_y) {
- numsteps = numsteps_x;
- deltax_step = (deltax < 0) ? -stepx : stepx;
- deltay_step = numsteps ? deltay / numsteps : deltay;
- } else { // numsteps_x <= numsteps_y
- numsteps = numsteps_y;
- deltay_step = (deltay < 0) ? -stepy : stepy;
- deltax_step = numsteps ? deltax / numsteps : deltax;
- }
-
-/* if (ABS(deltax) > ABS(deltay)) {*/ // Bresenham on y
- if (numsteps_y < numsteps_x) {
-
- 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
- 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);
- bdi = -ABS(deltay);
-
- }
-
- 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);
-
- //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 = readSelector(segMan, mover, SELECTOR(client));
+ int16 stepFactor = (argc >= 2) ? argv[1].toUint16() : 1;
+ int16 mover_x = readSelectorValue(segMan, mover, SELECTOR(x));
+ int16 mover_y = readSelectorValue(segMan, mover, SELECTOR(y));
+ int16 client_xStep = readSelectorValue(segMan, client, SELECTOR(xStep)) * stepFactor;
+ int16 client_yStep = readSelectorValue(segMan, client, SELECTOR(yStep)) * stepFactor;
+
+ int16 client_step;
+ if (client_xStep < client_yStep)
+ client_step = client_yStep * 2;
+ else
+ client_step = client_xStep * 2;
+
+ int16 deltaX = mover_x - readSelectorValue(segMan, client, SELECTOR(x));
+ int16 deltaY = mover_y - readSelectorValue(segMan, client, SELECTOR(y));
+ int16 mover_dx = 0;
+ int16 mover_dy = 0;
+ int16 mover_i1 = 0;
+ int16 mover_i2 = 0;
+ int16 mover_di = 0;
+ int16 mover_incr = 0;
+ int16 mover_xAxis = 0;
+
+ while (1) {
+ mover_dx = client_xStep;
+ mover_dy = client_yStep;
+ mover_incr = 1;
+
+ if (ABS(deltaX) >= ABS(deltaY)) {
+ mover_xAxis = 1;
+ if (deltaX < 0)
+ mover_dx = -mover_dx;
+ mover_dy = deltaX ? mover_dx * deltaY / deltaX : 0;
+ mover_i1 = ((mover_dx * deltaY) - (mover_dy * deltaX)) * 2;
+ if (deltaY < 0) {
+ mover_incr = -1;
+ mover_i1 = -mover_i1;
+ }
+ mover_i2 = mover_i1 - (deltaX * 2);
+ mover_di = mover_i1 - deltaX;
+ if (deltaX < 0) {
+ mover_i1 = -mover_i1;
+ mover_i2 = -mover_i2;
+ mover_di = -mover_di;
+ }
+ } else {
+ mover_xAxis = 0;
+ if (deltaY < 0)
+ mover_dy = -mover_dy;
+ mover_dx = deltaY ? mover_dy * deltaX / deltaY : 0;
+ mover_i1 = ((mover_dy * deltaX) - (mover_dx * deltaY)) * 2;
+ if (deltaX < 0) {
+ mover_incr = -1;
+ mover_i1 = -mover_i1;
+ }
+ mover_i2 = mover_i1 - (deltaY * 2);
+ mover_di = mover_i1 - deltaY;
+ if (deltaY < 0) {
+ mover_i1 = -mover_i1;
+ mover_i2 = -mover_i2;
+ mover_di = -mover_di;
+ }
+ break;
+ }
+ if (client_xStep <= client_yStep)
+ break;
+ if (!client_xStep)
+ break;
+ if (client_yStep >= ABS(mover_dy + mover_incr))
+ break;
+
+ client_step--;
+ if (!client_step)
+ error("kInitBresen failed");
+ client_xStep--;
+ }
- 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);
-
+ // set mover
+ writeSelectorValue(segMan, mover, SELECTOR(dx), mover_dx);
+ writeSelectorValue(segMan, mover, SELECTOR(dy), mover_dy);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i1), mover_i1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i2), mover_i2);
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), mover_di);
+ writeSelectorValue(segMan, mover, SELECTOR(b_incr), mover_incr);
+ writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), mover_xAxis);
return s->r_acc;
}
-#define MOVING_ON_X (((axis == _K_BRESEN_AXIS_X)&&bi1) || dx)
-#define MOVING_ON_Y (((axis == _K_BRESEN_AXIS_Y)&&bi1) || dy)
-
reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
reg_t mover = argv[0];
reg_t client = readSelector(segMan, mover, SELECTOR(client));
+ bool completed = false;
+ bool handleMoveCount = g_sci->_features->handleMoveCount();
- 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 = readSelectorValue(segMan, client, SELECTOR(signal));
- int completed = 0;
- int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed));
-
- if (getSciVersion() > SCI_VERSION_01)
- signal &= ~kSignalHitObstacle;
-
- writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0
- oldx = x;
- oldy = y;
- 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);
- }
+ if (getSciVersion() >= SCI_VERSION_1_EGA) {
+ uint client_signal = readSelectorValue(segMan, client, SELECTOR(signal));
+ writeSelectorValue(segMan, client, SELECTOR(signal), client_signal & ~kSignalHitObstacle);
+ }
+
+ int16 mover_moveCnt = 1;
+ int16 client_moveSpeed = 0;
+ if (handleMoveCount) {
+ mover_moveCnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt));
+ client_moveSpeed = readSelectorValue(segMan, client, SELECTOR(moveSpeed));
+ mover_moveCnt++;
}
- //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
+ if (client_moveSpeed < mover_moveCnt) {
+ mover_moveCnt = 0;
+ int16 client_x = readSelectorValue(segMan, client, SELECTOR(x));
+ int16 client_y = readSelectorValue(segMan, client, SELECTOR(y));
+ int16 client_org_x = client_x;
+ int16 client_org_y = client_y;
+ int16 mover_x = readSelectorValue(segMan, mover, SELECTOR(x));
+ int16 mover_y = readSelectorValue(segMan, mover, SELECTOR(y));
+ int16 mover_xAxis = readSelectorValue(segMan, mover, SELECTOR(b_xAxis));
+ int16 mover_dx = readSelectorValue(segMan, mover, SELECTOR(dx));
+ int16 mover_dy = readSelectorValue(segMan, mover, SELECTOR(dy));
+ int16 mover_incr = readSelectorValue(segMan, mover, SELECTOR(b_incr));
+ int16 mover_i1 = readSelectorValue(segMan, mover, SELECTOR(b_i1));
+ int16 mover_i2 = readSelectorValue(segMan, mover, SELECTOR(b_i2));
+ int16 mover_di = readSelectorValue(segMan, mover, SELECTOR(b_di));
+ int16 mover_org_i1 = mover_i1;
+ int16 mover_org_i2 = mover_i2;
+ int16 mover_org_di = mover_di;
+
+ if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
+ // save current position into mover
+ writeSelectorValue(segMan, mover, SELECTOR(xLast), client_x);
+ writeSelectorValue(segMan, mover, SELECTOR(yLast), client_y);
+ }
+ // sierra sci saves full client selector variables here
- if (g_sci->_features->handleMoveCount()) {
- if (max_movcnt > movcnt) {
- ++movcnt;
- writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
- return NULL_REG;
+ if (mover_xAxis) {
+ if (ABS(mover_x - client_x) < ABS(mover_dx))
+ completed = true;
} else {
- movcnt = 0;
- writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre?
+ if (ABS(mover_y - client_y) < ABS(mover_dy))
+ completed = true;
+ }
+ if (completed) {
+ client_x = mover_x;
+ client_y = mover_y;
+ } else {
+ client_x += mover_dx;
+ client_y += mover_dy;
+ if (mover_di < 0) {
+ mover_di += mover_i1;
+ } else {
+ mover_di += mover_i2;
+ if (mover_xAxis == 0) {
+ client_x += mover_incr;
+ } else {
+ client_y += mover_incr;
+ }
+ }
+ }
+ writeSelectorValue(segMan, client, SELECTOR(x), client_x);
+ writeSelectorValue(segMan, client, SELECTOR(y), client_y);
+
+ // Now call client::canBeHere/client::cantBehere to check for collisions
+ 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 {
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
+ if (s->r_acc.isNull())
+ collision = true;
}
- }
- if ((bdi += bi1) > 0) {
- bdi += bi2;
+ if (collision) {
+ // sierra restores full client variables here, seems that restoring x/y is enough
+ writeSelectorValue(segMan, client, SELECTOR(x), client_org_x);
+ writeSelectorValue(segMan, client, SELECTOR(y), client_org_y);
+ mover_i1 = mover_org_i1;
+ mover_i2 = mover_org_i2;
+ mover_di = mover_org_di;
- if (axis == _K_BRESEN_AXIS_X)
- dx += bdelta;
+ uint16 client_signal = readSelectorValue(segMan, client, SELECTOR(signal));
+ writeSelectorValue(segMan, client, SELECTOR(signal), client_signal | kSignalHitObstacle);
+ }
+ writeSelectorValue(segMan, mover, SELECTOR(b_i1), mover_i1);
+ writeSelectorValue(segMan, mover, SELECTOR(b_i2), mover_i2);
+ writeSelectorValue(segMan, mover, SELECTOR(b_di), mover_di);
+
+ if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
+ // this calling code here was right before the last return in
+ // sci1ega and got changed to this position since sci1early
+ // this was an uninitialized issue in sierra sci
+ if ((handleMoveCount) && (getSciVersion() >= SCI_VERSION_1_EARLY))
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt);
+ // We need to compare directly in here, complete may have happened during
+ // the current move
+ if ((client_x == mover_x) && (client_y == mover_y))
+ invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
+ if (getSciVersion() >= SCI_VERSION_1_EARLY)
+ return s->r_acc;
+ }
+ }
+ if (handleMoveCount) {
+ if (getSciVersion() <= SCI_VERSION_1_EGA)
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), mover_moveCnt);
else
- dy += bdelta;
+ writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), client_moveSpeed);
}
+ return s->r_acc;
+}
- writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi);
+extern void kDirLoopWorker(reg_t obj, uint16 angle, EngineState *s, int argc, reg_t *argv);
+extern uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2);
- x += dx;
- y += dy;
+reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
+ SegManager *segMan = s->_segMan;
+ reg_t avoider = argv[0];
+ int16 timesStep = argc > 1 ? argv[1].toUint16() : 1;
- if ((MOVING_ON_X && (((x < destx) && (oldx >= destx)) // Moving left, exceeded?
- || ((x > destx) && (oldx <= destx)) // Moving right, exceeded?
- || ((x == destx) && (ABS(dx) > ABS(dy))) // Moving fast, reached?
- // Treat this last case specially- when doing sub-pixel movements
- // on the other axis, we could still be far away from the destination
- )) || (MOVING_ON_Y && (((y < desty) && (oldy >= desty)) /* Moving upwards, exceeded? */
- || ((y > desty) && (oldy <= desty)) /* Moving downwards, exceeded? */
- || ((y == desty) && (ABS(dy) >= ABS(dx))) /* Moving fast, reached? */
- ))) {
- // Whew... in short: If we have reached or passed our target position
+ if (!s->_segMan->isHeapObject(avoider)) {
+ error("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
+ return SIGNAL_REG;
+ }
- x = destx;
- y = desty;
- completed = 1;
+ reg_t client = readSelector(segMan, avoider, SELECTOR(client));
+ reg_t mover = readSelector(segMan, client, SELECTOR(mover));
+ if (mover.isNull())
+ return SIGNAL_REG;
- debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover));
- }
+ // call mover::doit
+ invokeSelector(s, mover, SELECTOR(doit), argc, argv);
- writeSelectorValue(segMan, client, SELECTOR(x), x);
- writeSelectorValue(segMan, client, SELECTOR(y), y);
+ // Read mover again
+ mover = readSelector(segMan, client, SELECTOR(mover));
+ if (mover.isNull())
+ return SIGNAL_REG;
- debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi);
+ int16 clientX = readSelectorValue(segMan, client, SELECTOR(x));
+ int16 clientY = readSelectorValue(segMan, client, SELECTOR(y));
+ int16 moverX = readSelectorValue(segMan, mover, SELECTOR(x));
+ int16 moverY = readSelectorValue(segMan, mover, SELECTOR(y));
+ int16 avoiderHeading = readSelectorValue(segMan, avoider, SELECTOR(heading));
- bool collision = false;
- reg_t cantBeHere = NULL_REG;
+ // call client::isBlocked
+ invokeSelector(s, client, SELECTOR(isBlocked), argc, argv);
- 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 {
- invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
- if (s->r_acc.isNull())
- collision = true;
- }
+ if (s->r_acc.isNull()) {
+ // not blocked
+ if (avoiderHeading == -1)
+ return SIGNAL_REG;
+ avoiderHeading = -1;
- if (collision) {
- signal = readSelectorValue(segMan, client, SELECTOR(signal));
+ uint16 angle = kGetAngleWorker(clientX, clientY, moverX, moverY);
- writeSelectorValue(segMan, client, SELECTOR(x), oldx);
- writeSelectorValue(segMan, client, SELECTOR(y), oldy);
- writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
+ reg_t clientLooper = readSelector(segMan, client, SELECTOR(looper));
+ if (clientLooper.isNull()) {
+ kDirLoopWorker(client, angle, s, argc, argv);
+ } else {
+ // call looper::doit
+ reg_t params[2] = { make_reg(0, angle), client };
+ invokeSelector(s, clientLooper, SELECTOR(doit), argc, argv, 2, params);
+ }
+ s->r_acc = SIGNAL_REG;
+
+ } else {
+ // is blocked
+ if (avoiderHeading == -1)
+ avoiderHeading = g_sci->getRNG().getRandomBit() ? 45 : -45;
+ int16 clientHeading = readSelectorValue(segMan, client, SELECTOR(heading));
+ clientHeading = (clientHeading / 45) * 45;
+
+ int16 clientXstep = readSelectorValue(segMan, client, SELECTOR(xStep)) * timesStep;
+ int16 clientYstep = readSelectorValue(segMan, client, SELECTOR(yStep)) * timesStep;
+ int16 newHeading = clientHeading;
+
+ while (1) {
+ int16 newX = clientX;
+ int16 newY = clientY;
+ switch (newHeading) {
+ case 45:
+ case 90:
+ case 135:
+ newX += clientXstep;
+ break;
+ case 225:
+ case 270:
+ case 315:
+ newX -= clientXstep;
+ }
- debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover));
- // 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
- }
+ switch (newHeading) {
+ case 0:
+ case 45:
+ case 315:
+ newY -= clientYstep;
+ break;
+ case 135:
+ case 180:
+ case 225:
+ newY += clientYstep;
+ }
+ writeSelectorValue(segMan, client, SELECTOR(x), newX);
+ writeSelectorValue(segMan, client, SELECTOR(y), newY);
- if ((getSciVersion() >= SCI_VERSION_1_EGA))
- if (completed)
- invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
+ // call client::canBeHere
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
- if (SELECTOR(cantBeHere) != -1)
- return cantBeHere;
- return make_reg(0, completed);
-}
+ if (!s->r_acc.isNull()) {
+ s->r_acc = make_reg(0, newHeading);
+ break; // break out
+ }
-extern void _k_dirloop(reg_t obj, uint16 angle, EngineState *s, int argc, reg_t *argv);
-
-int getAngle(int xrel, int yrel) {
- if ((xrel == 0) && (yrel == 0))
- return 0;
- else {
- int val = (int)(180.0 / PI * atan2((double)xrel, (double) - yrel));
- if (val < 0)
- val += 360;
-
- // Take care of OB1 differences between SSCI and
- // FSCI. SCI games sometimes check for equality with
- // "round" angles
- if (val % 45 == 44)
- val++;
- else if (val % 45 == 1)
- val--;
-
- return val;
+ newHeading += avoiderHeading;
+ if (newHeading >= 360)
+ newHeading -= 360;
+ if (newHeading < 0)
+ newHeading += 360;
+ if (newHeading == clientHeading) {
+ // tried everything
+ writeSelectorValue(segMan, client, SELECTOR(x), clientX);
+ writeSelectorValue(segMan, client, SELECTOR(y), clientY);
+ s->r_acc = SIGNAL_REG;
+ break; // break out
+ }
+ }
}
-}
+ writeSelectorValue(segMan, avoider, SELECTOR(heading), avoiderHeading);
+ return s->r_acc;
-reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
- SegManager *segMan = s->_segMan;
- reg_t avoider = argv[0];
+#if 0
reg_t client, looper, mover;
int angle;
int dx, dy;
@@ -486,18 +601,18 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, angle);
- reg_t params[2] = { make_reg(0, angle), client };
-
if (looper.segment) {
+ reg_t params[2] = { make_reg(0, angle), client };
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);
+ kDirLoopWorker(client, (uint16)angle, s, argc, argv);
}
}
return s->r_acc;
+#endif
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 552e425906..6a052a582d 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -93,13 +93,13 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
reg_t stringpos = argv[0];
Common::String string = s->_segMan->getString(stringpos);
char *error;
- ResultWordList words;
reg_t event = argv[1];
g_sci->checkVocabularySwitch();
Vocabulary *voc = g_sci->getVocabulary();
voc->parser_event = event;
reg_t params[2] = { voc->parser_base, stringpos };
+ ResultWordListList words;
bool res = voc->tokenizeString(words, string.c_str(), &error);
voc->parserIsValid = false; /* not valid */
@@ -109,10 +109,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, 1);
#ifdef DEBUG_PARSER
- 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);
+ debugC(2, kDebugLevelParser, "Parsed to the following blocks:");
+
+ for (ResultWordListList::const_iterator i = words.begin(); i != words.end(); ++i) {
+ debugCN(2, kDebugLevelParser, " ");
+ for (ResultWordList::const_iterator j = i->begin(); j != i->end(); ++j) {
+ debugCN(2, kDebugLevelParser, "%sType[%04x] Group[%04x]", j == i->begin() ? "" : " / ", j->_class, j->_group);
+ }
+ debugCN(2, kDebugLevelParser, "\n");
+ }
#endif
int syntax_fail = voc->parseGNF(words);
@@ -138,6 +143,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
} else {
s->r_acc = make_reg(0, 0);
+ // FIXME: When typing something wrong in the fanmade game Demo Quest,
+ // after the error dialog, the game checks for claimed to be 0 before
+ // showing a subsequent dialog. The following selector change causes
+ // it to be 1, thus causing the game to hang in an endless loop (bug
+ // #3038870. Thus, this seems to be wrong (since fanmade games use
+ // the original SCI interpreter), but we need to check against
+ // dissassembly. Since kParse is in the process of being dissassembled
+ // again, I'm leaving this FIXME in for now, so that it won't be
+ // forgotten :)
writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
if (error) {
s->_segMan->strcpy(voc->parser_base, error);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 07d0a31f0b..faf966af92 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -261,13 +261,7 @@ struct PathfindingState {
int findNearPoint(const Common::Point &p, Polygon *polygon, Common::Point *ret);
};
-
-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) {
- // 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));
- }
+static Common::Point readPoint(SegmentRef list_r, int offset) {
Common::Point point;
if (list_r.isRaw) {
@@ -350,10 +344,16 @@ static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) {
Common::Point first, prev;
int i;
- prev = first = read_point(segMan, points, 0);
+ SegmentRef pointList = segMan->dereference(points);
+ if (!pointList.isValid() || pointList.skipByte) {
+ warning("draw_polygon: Polygon data pointer is invalid, skipping polygon");
+ return;
+ }
+
+ prev = first = readPoint(pointList, 0);
for (i = 1; i < size; i++) {
- Common::Point point = read_point(segMan, points, i);
+ Common::Point point = readPoint(pointList, i);
draw_line(s, prev, point, type, width, height);
prev = point;
}
@@ -401,12 +401,18 @@ static void print_polygon(SegManager *segMan, reg_t polygon) {
debugN(-1, "%i:", type);
+ SegmentRef pointList = segMan->dereference(points);
+ if (!pointList.isValid() || pointList.skipByte) {
+ warning("print_polygon: Polygon data pointer is invalid, skipping polygon");
+ return;
+ }
+
for (i = 0; i < size; i++) {
- point = read_point(segMan, points, i);
+ point = readPoint(pointList, i);
debugN(-1, " (%i, %i)", point.x, point.y);
}
- point = read_point(segMan, points, 0);
+ point = readPoint(pointList, 0);
debug(" (%i, %i);", point.x, point.y);
}
@@ -1092,7 +1098,22 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
return NULL;
}
- Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type)));
+ SegmentRef pointList = segMan->dereference(points);
+ // Check if the target polygon is still valid. It may have been released
+ // in the meantime (e.g. in LSL6, room 700, when using the elevator).
+ // Refer to bug #3034501.
+ if (!pointList.isValid() || pointList.skipByte) {
+ warning("convert_polygon: Polygon data pointer is invalid, skipping polygon");
+ return NULL;
+ }
+
+ // Make sure that we have enough points
+ if (pointList.maxSize < size * POLY_POINT_SIZE) {
+ warning("convert_polygon: Not enough memory allocated for polygon points. "
+ "Expected %d, got %d. Skipping polygon",
+ size * POLY_POINT_SIZE, pointList.maxSize);
+ return NULL;
+ }
int skip = 0;
@@ -1100,14 +1121,16 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
// Polygon has 17 points but size is set to 19
if ((size == 19) && g_sci->getGameId() == GID_LSL1) {
if ((s->currentRoomNumber() == 350)
- && (read_point(segMan, points, 18) == Common::Point(108, 137))) {
+ && (readPoint(pointList, 18) == Common::Point(108, 137))) {
debug(1, "Applying fix for broken polygon in lsl1sci, room 350");
size = 17;
}
}
+ Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type)));
+
for (i = skip; i < size; i++) {
- Vertex *vertex = new Vertex(read_point(segMan, points, i));
+ Vertex *vertex = new Vertex(readPoint(pointList, i));
poly->vertices.insertHead(vertex);
}
@@ -1163,7 +1186,9 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
Node *node = s->_segMan->lookupNode(list->first);
while (node) {
- polygon = convert_polygon(s, node->value);
+ // The node value might be null, in which case there's no polygon to parse.
+ // Happens in LB2 floppy - refer to bug #3041232
+ polygon = !node->value.isNull() ? convert_polygon(s, node->value) : NULL;
if (polygon) {
pf_s->polygons.push_back(polygon);
@@ -1402,8 +1427,15 @@ static reg_t output_path(PathfindingState *p, EngineState *s) {
if (DebugMan.isDebugChannelEnabled(kDebugLevelAvoidPath)) {
debug("\nReturning path:");
+
+ SegmentRef outputList = s->_segMan->dereference(output);
+ if (!outputList.isValid() || outputList.skipByte) {
+ warning("output_path: Polygon data pointer is invalid, skipping polygon");
+ return output;
+ }
+
for (int i = 0; i < offset; i++) {
- Common::Point pt = read_point(s->_segMan, output, i);
+ Common::Point pt = readPoint(outputList, i);
debugN(-1, " (%i, %i)", pt.x, pt.y);
}
debug(";\n");
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index a5501c160f..e7f466f9a2 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -143,56 +143,72 @@ 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];
- const Object *parent_obj = s->_segMan->getObject(parent_addr);
- reg_t clone_addr;
- Clone *clone_obj; // same as Object*
+ reg_t parentAddr = argv[0];
+ const Object *parentObj = s->_segMan->getObject(parentAddr);
+ reg_t cloneAddr;
+ Clone *cloneObj; // same as Object*
- if (!parent_obj) {
- error("Attempt to clone non-object/class at %04x:%04x failed", PRINT_REG(parent_addr));
+ if (!parentObj) {
+ error("Attempt to clone non-object/class at %04x:%04x failed", PRINT_REG(parentAddr));
return NULL_REG;
}
- debugC(2, kDebugLevelMemory, "Attempting to clone from %04x:%04x", PRINT_REG(parent_addr));
+ debugC(2, kDebugLevelMemory, "Attempting to clone from %04x:%04x", PRINT_REG(parentAddr));
- clone_obj = s->_segMan->allocateClone(&clone_addr);
+ uint16 infoSelector = readSelectorValue(s->_segMan, parentAddr, SELECTOR(_info_));
+ cloneObj = s->_segMan->allocateClone(&cloneAddr);
- if (!clone_obj) {
- error("Cloning %04x:%04x failed-- internal error", PRINT_REG(parent_addr));
+ if (!cloneObj) {
+ error("Cloning %04x:%04x failed-- internal error", PRINT_REG(parentAddr));
return NULL_REG;
}
- *clone_obj = *parent_obj;
+ // In case the parent object is a clone itself we need to refresh our
+ // pointer to it here. This is because calling allocateClone might
+ // invalidate all pointers, references and iterators to data in the clones
+ // segment.
+ //
+ // The reason why it might invalidate those is, that the segment code
+ // (Table) uses Common::Array for internal storage. Common::Array now
+ // might invalidate references to its contained data, when it has to
+ // extend the internal storage size.
+ if (infoSelector & kInfoFlagClone)
+ parentObj = s->_segMan->getObject(parentAddr);
+
+ *cloneObj = *parentObj;
// Mark as clone
- clone_obj->markAsClone();
- clone_obj->setSpeciesSelector(clone_obj->getPos());
- if (parent_obj->isClass())
- clone_obj->setSuperClassSelector(parent_obj->getPos());
- s->_segMan->getScript(parent_obj->getPos().segment)->incrementLockers();
- s->_segMan->getScript(clone_obj->getPos().segment)->incrementLockers();
-
- return clone_addr;
+ infoSelector &= ~kInfoFlagClass; // remove class bit
+ writeSelectorValue(s->_segMan, cloneAddr, SELECTOR(_info_), infoSelector | kInfoFlagClone);
+
+ cloneObj->setSpeciesSelector(cloneObj->getPos());
+ if (parentObj->isClass())
+ cloneObj->setSuperClassSelector(parentObj->getPos());
+ s->_segMan->getScript(parentObj->getPos().segment)->incrementLockers();
+ s->_segMan->getScript(cloneObj->getPos().segment)->incrementLockers();
+
+ return cloneAddr;
}
extern void _k_view_list_mark_free(EngineState *s, reg_t off);
reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
- reg_t victim_addr = argv[0];
- Clone *victim_obj = s->_segMan->getObject(victim_addr);
+ reg_t obj = argv[0];
+ Clone *object = s->_segMan->getObject(obj);
- if (!victim_obj) {
+ if (!object) {
error("Attempt to dispose non-class/object at %04x:%04x",
- PRINT_REG(victim_addr));
- return s->r_acc;
- }
-
- if (!victim_obj->isClone()) {
- // SCI silently ignores this behaviour; some games actually depend on it
+ PRINT_REG(obj));
return s->r_acc;
}
- victim_obj->markAsFreed();
+ // SCI uses this technique to find out, if it's a clone and if it's supposed to get freed
+ // At least kq4early relies on this behaviour. The scripts clone "Sound", then set bit 1 manually
+ // and call kDisposeClone later. In that case we may not free it, otherwise we will run into issues
+ // later, because kIsObject would then return false and Sound object wouldn't get checked.
+ uint16 infoSelector = readSelectorValue(s->_segMan, obj, SELECTOR(_info_));
+ if ((infoSelector & 3) == kInfoFlagClone)
+ object->markAsFreed();
return s->r_acc;
}
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 9254bce9c1..5ea3178ae5 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -275,7 +275,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
reg = readSelector(s->_segMan, reg, SELECTOR(data));
#endif
- Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg,
+ Common::String tempsource = g_sci->getKernel()->lookupText(reg,
arguments[paramindex + 1]);
int slen = strlen(tempsource.c_str());
int extralen = str_leng - slen;
@@ -486,10 +486,12 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
}
#endif
- if ((func != K_MESSAGE_NEXT) && (argc < 2)) {
- warning("Message: not enough arguments passed to subfunction %d", func);
- return NULL_REG;
- }
+// TODO: Perhaps fix this check, currently doesn't work with PUSH and POP subfunctions
+// Pepper uses them to to handle the glossary
+// if ((func != K_MESSAGE_NEXT) && (argc < 2)) {
+// warning("Message: not enough arguments passed to subfunction %d", func);
+// return NULL_REG;
+// }
MessageTuple tuple;
@@ -558,6 +560,12 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+ case K_MESSAGE_PUSH:
+ s->_msgState->pushCursorStack();
+ break;
+ case K_MESSAGE_POP:
+ s->_msgState->popCursorStack();
+ break;
default:
warning("Message: subfunction %i invoked (not implemented)", func);
}
@@ -779,6 +787,20 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+/**
+ * Debug function, used in the demo of Shivers. It's marked as a stub
+ * in the original interpreter, but it gets called by the game scripts.
+ */
+reg_t kPrintDebug(EngineState *s, int argc, reg_t *argv) {
+ Common::String debugTemplate = s->_segMan->getString(argv[0]);
+ char debugString[500];
+
+ sprintf(debugString, debugTemplate.c_str(), argv[1].toUint16());
+ debugC(2, "kPrintDebug: \"%s\"\n", debugString);
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index ac6cfb6835..e97ae38702 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -117,7 +117,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
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.");
+ error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode");
return NULL_REG;
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index dfc41cc56a..87e328592f 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -45,8 +45,6 @@
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
-#include "gui/message.h"
-
namespace Sci {
@@ -568,26 +566,32 @@ void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette)
}
void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
- if (s.getVersion() < 24)
- return;
-
- if (s.isLoading() && _palVaryResourceId != -1)
- palVaryRemoveTimer();
-
- 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);
+ if (s.getVersion() >= 25) {
+ // We need to save intensity of the _sysPalette at least for kq6 when entering the dark cave (room 390)
+ // from room 340. scripts will set intensity to 60 for this room and restore them when leaving.
+ // Sierra SCI is also doing this (although obviously not for SCI0->SCI01 games, still it doesn't hurt
+ // to save it everywhere). ffs. bug #3072868
+ s.syncBytes(_sysPalette.intensity, 256);
}
+ if (s.getVersion() >= 24) {
+ if (s.isLoading() && _palVaryResourceId != -1)
+ palVaryRemoveTimer();
+
+ 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);
+ }
- if (s.isLoading() && _palVaryResourceId != -1) {
- _palVarySignal = 0;
- palVaryInstallTimer();
+ if (s.isLoading() && _palVaryResourceId != -1) {
+ _palVarySignal = 0;
+ palVaryInstallTimer();
+ }
}
}
@@ -701,6 +705,8 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
return true;
}
+extern void showScummVMDialog(const Common::String &message);
+
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
SavegameMetadata meta;
@@ -708,7 +714,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
sync_SavegameMetadata(ser, meta);
if (fh->eos()) {
- s->r_acc = make_reg(0, 1); // signal failure
+ s->r_acc = TRUE_REG; // signal failure
return;
}
@@ -721,10 +727,9 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
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();
+ showScummVMDialog("The format of this saved game is obsolete, unable to load it");
- s->r_acc = make_reg(0, 1); // signal failure
+ s->r_acc = TRUE_REG; // signal failure
return;
}
@@ -733,10 +738,9 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
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");
- GUI::MessageDialog dialog("This saved game was created with a different version of the game, unable to load it", "OK");
- dialog.runModal();
+ showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
- s->r_acc = make_reg(0, 1); // signal failure
+ s->r_acc = TRUE_REG; // signal failure
return;
}
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index fc254ba33d..14eec4aafc 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,7 +36,7 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 24,
+ CURRENT_SAVEGAME_VERSION = 25,
MINIMUM_SAVEGAME_VERSION = 14
};
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index f4129bb1ea..da9ab5106d 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -69,6 +69,9 @@ void Script::freeScript() {
void Script::init(int script_nr, ResourceManager *resMan) {
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
+ if (!script)
+ error("Script %d not found\n", script_nr);
+
_localsOffset = 0;
_localsBlock = NULL;
_localsCount = 0;
@@ -288,7 +291,8 @@ void Script::relocate(reg_t block) {
// 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.
+ // 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))
@@ -329,13 +333,13 @@ uint16 Script::validateExportFunc(int pubfunct) {
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
- if (g_sci->getGameId() != GID_CAMELOT) // cheap fix
- return offset;
- // we are getting assert()s in eco quest 1 (right on startup) and kq6 and maybe more
- // [md5] plz look into this TODO FIXME
+ // Check if the offset found points to a second export table (e.g. script 912
+ // in Camelot and script 306 in KQ4). Such offsets are usually small (i.e. < 10),
+ // thus easily distinguished from actual code offsets.
+ // This only makes sense for SCI0-SCI1, as the export table in SCI1.1+ games
+ // is located at a specific address, thus findBlock() won't work.
+ // Fixes bugs #3039785 and #3037595.
+ if (offset < 10 && getSciVersion() <= SCI_VERSION_1_LATE) {
const uint16 *secondExportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS, 0);
if (secondExportTable) {
@@ -532,7 +536,7 @@ void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
// 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,
+ // Example test case - 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();
@@ -554,6 +558,13 @@ void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
}
+void Script::initialiseObjects(SegManager *segMan, SegmentId segmentId) {
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ initialiseObjectsSci11(segMan, segmentId);
+ else
+ initialiseObjectsSci0(segMan, segmentId);
+}
+
reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
addr.offset = 0;
return addr;
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index c60cc4b19f..e316fc0c8d 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -159,14 +159,7 @@ public:
* @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);
+ void initialiseObjects(SegManager *segMan, SegmentId segmentId);
// script lock operations
@@ -260,6 +253,20 @@ private:
void relocate(reg_t block);
bool relocateLocal(SegmentId segment, int location);
+
+ /**
+ * 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);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 77818dd138..42d7c4d9cd 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -25,25 +25,34 @@
#include "sci/sci.h"
#include "sci/engine/script.h"
+#include "sci/engine/state.h"
#include "common/util.h"
namespace Sci {
#define PATCH_END 0xFFFF
-#define PATCH_ADDTOOFFSET 0x8000
-#define PATCH_GETORIGINALBYTE 0x4000
+#define PATCH_COMMANDMASK 0xF000
+#define PATCH_VALUEMASK 0x0FFF
+#define PATCH_ADDTOOFFSET 0xE000
+#define PATCH_GETORIGINALBYTE 0xD000
+#define PATCH_ADJUSTWORD 0xC000
+#define PATCH_ADJUSTWORD_NEG 0xB000
#define PATCH_MAGICDWORD(a, b, c, d) CONSTANT_LE_32(a | (b << 8) | (c << 16) | (d << 24))
+#define PATCH_VALUELIMIT 4096
struct SciScriptSignature {
uint16 scriptNr;
const char *description;
+ int16 applyCount;
uint32 magicDWord;
int magicOffset;
const byte *data;
const uint16 *patch;
};
+#define SCI_SIGNATUREENTRY_TERMINATOR { 0, NULL, 0, 0, 0, NULL, NULL }
+
// signatures are built like this:
// - first a counter of the bytes that follow
// - then the actual bytes that need to get matched
@@ -52,6 +61,51 @@ struct SciScriptSignature {
// - rinse and repeat
// ===========================================================================
+// Castle of Dr. Brain
+// cipher::init (script 391) is called on room 380 init. This resets the word
+// cipher puzzle. The puzzle sadly operates on some hep strings, which aren't
+// saved in our sci. So saving/restoring in this room will break the puzzle
+// Because of this issue, we just init the puzzle each time it's accessed.
+// this is not 100% sierra behaviour, in fact we will actually reset the puzzle
+// during each access which makes it impossible to cheat.
+const byte castlebrainSignatureCipherPuzzle[] = {
+ 22,
+ 0x35, 0x00, // ldi 00
+ 0xa3, 0x26, // sal local[26]
+ 0xa3, 0x25, // sal local[25]
+ 0x35, 0x00, // ldi 00
+ 0xa3, 0x2a, // sal local[2a] (local is not used)
+ 0xa3, 0x29, // sal local[29] (local is not used)
+ 0x35, 0xff, // ldi ff
+ 0xa3, 0x2c, // sal local[2c]
+ 0xa3, 0x2b, // sal local[2b]
+ 0x35, 0x00, // ldi 00
+ 0x65, 0x16, // aTop highlightedIcon
+ 0
+};
+
+const uint16 castlebrainPatchCipherPuzzle[] = {
+ 0x39, 0x6b, // pushi 6b (selector init)
+ 0x76, // push0
+ 0x55, 0x04, // self 04
+ 0x35, 0x00, // ldi 00
+ 0xa3, 0x25, // sal local[25]
+ 0xa3, 0x26, // sal local[26]
+ 0xa3, 0x29, // sal local[29]
+ 0x65, 0x16, // aTop highlightedIcon
+ 0x34, 0xff, 0xff, // ldi ffff
+ 0xa3, 0x2b, // sal local[2b]
+ 0xa3, 0x2c, // sal local[2c]
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature castlebrainSignatures[] = {
+ { 391, "cipher puzzle save/restore break", 1, PATCH_MAGICDWORD(0xa3, 0x26, 0xa3, 0x25), -2, castlebrainSignatureCipherPuzzle, castlebrainPatchCipherPuzzle },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// stayAndHelp::changeState (0) is called when ego swims to the left or right
// boundaries of room 660. Normally a textbox is supposed to get on screen
// but the call is wrong, so not only do we get an error message the script
@@ -73,7 +127,7 @@ const byte ecoquest1SignatureStayAndHelp[] = {
0x78, // push1
0x76, // push0
0x81, 0x00, // lag global[0]
- 0x4a, 0x06, // send 06 - ego::setMotion(0)
+ 0x4a, 0x06, // send 06 - call ego::setMotion(0)
0x39, 0x6e, // pushi 6e (selector init)
0x39, 0x04, // pushi 04
0x76, // push0
@@ -81,7 +135,7 @@ const byte ecoquest1SignatureStayAndHelp[] = {
0x39, 0x17, // pushi 17
0x7c, // pushSelf
0x51, 0x82, // class EcoNarrator
- 0x4a, 0x0c, // send 0c - EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!)
+ 0x4a, 0x0c, // send 0c - call EcoNarrator::init(0, 0, 23, self) (BADLY BROKEN!)
0x33, // jmp [end]
0
};
@@ -97,7 +151,7 @@ const uint16 ecoquest1PatchStayAndHelp[] = {
0x78, // push1
0x76, // push0
0x81, 0x00, // lag global[0]
- 0x4a, 0x06, // send 06 - ego::setMotion(0)
+ 0x4a, 0x06, // send 06 - call ego::setMotion(0)
0x39, 0x6e, // pushi 6e (selector init)
0x39, 0x06, // pushi 06
0x39, 0x02, // pushi 02 (additional 2 bytes)
@@ -107,16 +161,174 @@ const uint16 ecoquest1PatchStayAndHelp[] = {
0x7c, // pushSelf
0x38, 0x80, 0x02, // pushi 280 (additional 3 bytes)
0x51, 0x82, // class EcoNarrator
- 0x4a, 0x10, // send 10 - EcoNarrator::init(2, 0, 0, 23, self, 640)
+ 0x4a, 0x10, // send 10 - call EcoNarrator::init(2, 0, 0, 23, self, 640)
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature ecoquest1Signatures[] = {
- { 660, "CD: bad messagebox and freeze", PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp },
- { 0, NULL, 0, 0, NULL, NULL }
+ { 660, "CD: bad messagebox and freeze", 1, PATCH_MAGICDWORD(0x38, 0x22, 0x01, 0x78), -17, ecoquest1SignatureStayAndHelp, ecoquest1PatchStayAndHelp },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
+// ===========================================================================
+// doMyThing::changeState (2) is supposed to remove the initial text on the
+// ecorder. This is done by reusing temp-space, that was filled on state 1.
+// this worked in sierra sci just by accident. In our sci, the temp space
+// is resetted every time, which means the previous text isn't available
+// anymore. We have to patch the code because of that ffs. bug #3035386
+const byte ecoquest2SignatureEcorder[] = {
+ 35,
+ 0x31, 0x22, // bnt [next state]
+ 0x39, 0x0a, // pushi 0a
+ 0x5b, 0x04, 0x1e, // lea temp[1e]
+ 0x36, // push
+ 0x39, 0x64, // pushi 64
+ 0x39, 0x7d, // pushi 7d
+ 0x39, 0x32, // pushi 32
+ 0x39, 0x66, // pushi 66
+ 0x39, 0x17, // pushi 17
+ 0x39, 0x69, // pushi 69
+ 0x38, 0x31, 0x26, // pushi 2631
+ 0x39, 0x6a, // pushi 6a
+ 0x39, 0x64, // pushi 64
+ 0x43, 0x1b, 0x14, // call kDisplay
+ 0x35, 0x0a, // ldi 0a
+ 0x65, 0x20, // aTop ticks
+ 0x33, // jmp [end]
+ +1, 5, // [skip 1 byte]
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, // bnt [end]
+ 0
+};
+
+const uint16 ecoquest2PatchEcorder[] = {
+ 0x2f, 0x02, // bt [to pushi 07]
+ 0x3a, // toss
+ 0x48, // ret
+ 0x38, 0x07, 0x00, // pushi 07 (parameter count) (waste 1 byte)
+ 0x39, 0x0b, // push (FillBoxAny)
+ 0x39, 0x1d, // pushi 29d
+ 0x39, 0x73, // pushi 115d
+ 0x39, 0x5e, // pushi 94d
+ 0x38, 0xd7, 0x00, // pushi 215d
+ 0x78, // push1 (visual screen)
+ 0x38, 0x17, 0x00, // pushi 17 (color) (waste 1 byte)
+ 0x43, 0x6c, 0x0e, // call kGraph
+ 0x38, 0x05, 0x00, // pushi 05 (parameter count) (waste 1 byte)
+ 0x39, 0x0c, // pushi 12d (UpdateBox)
+ 0x39, 0x1d, // pushi 29d
+ 0x39, 0x73, // pushi 115d
+ 0x39, 0x5e, // pushi 94d
+ 0x38, 0xd7, 0x00, // pushi 215d
+ 0x43, 0x6c, 0x0a, // call kGraph
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature ecoquest2Signatures[] = {
+ { 50, "initial text not removed on ecorder", 1, PATCH_MAGICDWORD(0x39, 0x64, 0x39, 0x7d), -8, ecoquest2SignatureEcorder, ecoquest2PatchEcorder },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
+// script 0 of freddy pharkas/CD PointsSound::check waits for a signal and if
+// no signal received will call kDoSound(0xD) which is a dummy in sierra sci
+// and ScummVM and will use acc (which is not set by the dummy) to trigger
+// sound disposal. This somewhat worked in sierra sci, because the sample
+// was already playing in the sound driver. In our case we would also stop
+// the sample from playing, so we patch it out
+// The "score" code is already buggy and sets volume to 0 when playing
+const byte freddypharkasSignatureScoreDisposal[] = {
+ 10,
+ 0x67, 0x32, // pTos 32 (selector theAudCount)
+ 0x78, // push1
+ 0x39, 0x0d, // pushi 0d
+ 0x43, 0x75, 0x02, // call kDoAudio
+ 0x1c, // ne?
+ 0x31, // bnt (-> to skip disposal)
+ 0
+};
+
+const uint16 freddypharkasPatchScoreDisposal[] = {
+ 0x34, 0x00, 0x00, // ldi 0000
+ 0x34, 0x00, 0x00, // ldi 0000
+ 0x34, 0x00, 0x00, // ldi 0000
+ PATCH_END
+};
+
+// script 235 of freddy pharkas rm235::init and sEnterFrom500::changeState
+// disable icon 7+8 of iconbar (CD only). When picking up the cannister after
+// placing it down, the scripts will disable all the other icons. This results
+// in IconBar::disable doing endless loops even in sierra sci, because there
+// is no enabled icon left. We remove disabling of icon 8 (which is help),
+// this fixes the issue.
+const byte freddypharkasSignatureCannisterHang[] = {
+ 12,
+ 0x38, 0xf1, 0x00, // pushi f1 (selector disable)
+ 0x7a, // push2
+ 0x39, 0x07, // pushi 07
+ 0x39, 0x08, // pushi 08
+ 0x81, 0x45, // lag 45
+ 0x4a, 0x08, // send 08 - call IconBar::disable(7, 8)
+ 0
+};
+
+const uint16 freddypharkasPatchCannisterHang[] = {
+ PATCH_ADDTOOFFSET | +3,
+ 0x78, // push1
+ PATCH_ADDTOOFFSET | +2,
+ 0x33, 0x00, // ldi 00 (waste 2 bytes)
+ PATCH_ADDTOOFFSET | +3,
+ 0x06, // send 06 - call IconBar::disable(7)
+ PATCH_END
+};
+
+// script 215 of freddy pharkas lowerLadder::doit and highLadder::doit actually
+// process keyboard-presses when the ladder is on the screen in that room.
+// They strangely also call kGetEvent. Because the main User::doit also calls
+// kGetEvent, it's pure luck, where the event will hit. It's the same issue
+// as in QfG1VGA and if you turn dos-box to max cycles, and click around for
+// ego, sometimes clicks also won't get registered. Strangely it's not nearly
+// as bad as in our sci, but these differences may be caused by timing.
+// We just reuse the active event, thus removing the duplicate kGetEvent call.
+const byte freddypharkasSignatureLadderEvent[] = {
+ 21,
+ 0x39, 0x6d, // pushi 6d (selector new)
+ 0x76, // push0
+ 0x38, 0xf5, 0x00, // pushi f5 (selector curEvent)
+ 0x76, // push0
+ 0x81, 0x50, // lag global[50]
+ 0x4a, 0x04, // send 04 - read User::curEvent
+ 0x4a, 0x04, // send 04 - call curEvent::new
+ 0xa5, 0x00, // sat temp[0]
+ 0x38, 0x94, 0x00, // pushi 94 (selector localize)
+ 0x76, // push0
+ 0x4a, 0x04, // send 04 - call curEvent::localize
+ 0
+};
+
+const uint16 freddypharkasPatchLadderEvent[] = {
+ 0x34, 0x00, 0x00, // ldi 0000 (waste 3 bytes, overwrites first 2 pushes)
+ PATCH_ADDTOOFFSET | +8,
+ 0xa5, 0x00, // sat temp[0] (waste 2 bytes, overwrites 2nd send)
+ PATCH_ADDTOOFFSET | +2,
+ 0x34, 0x00, 0x00, // ldi 0000
+ 0x34, 0x00, 0x00, // ldi 0000 (waste 6 bytes, overwrites last 3 opcodes)
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature freddypharkasSignatures[] = {
+ { 0, "CD: score early disposal", 1, PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
+ { 235, "CD: cannister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCannisterHang, freddypharkasPatchCannisterHang },
+ { 320, "ladder event issue", 2, PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// daySixBeignet::changeState (4) 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[] = {
@@ -182,12 +394,12 @@ const uint16 gk1PatchDay5PhoneFreeze[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature gk1Signatures[] = {
- { 212, "day 5 phone freeze", PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
- { 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 }
+ { 212, "day 5 phone freeze", 1, PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
+ { 230, "day 6 police beignet timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
+ { 230, "day 6 police sleep timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -196,63 +408,63 @@ const SciScriptSignature gk1Signatures[] = {
// 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
-};
+//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 - call song::stop
+// 0x39, 0x27, // pushi 27
+// 0x78, // push1
+// 0x8f, 0x01, // lsp 01
+// 0x51, 0x54, // class 54
+// 0x4a, 0x06, // send 06 - call 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 }
-};
+//const SciScriptSignature hoyle4Signatures[] = {
+// { 0, "port fix when disposing windows", PATCH_MAGICDWORD(0x64, 0x38, 0xC8, 0x00), -5, hoyle4SignaturePortFix, hoyle4PatchPortFix },
+// { 0, NULL, 0, 0, NULL, NULL }
+//};
// ===========================================================================
// at least during harpy scene export 29 of script 0 is called in kq5cd and
@@ -270,12 +482,12 @@ const byte kq5SignatureCdHarpyVolume[] = {
0x38, 0x7b, 0x01, // pushi 017b
0x76, // push0
0x81, 0x01, // lag global[1]
- 0x4a, 0x04, // send 04 (getting KQ5::masterVolume)
+ 0x4a, 0x04, // send 04 - read KQ5::masterVolume
0xa5, 0x03, // sat temp[3] (store volume in temp 3)
0x38, 0x7b, 0x01, // pushi 017b
0x76, // push0
0x81, 0x01, // lag global[1]
- 0x4a, 0x04, // send 04 (getting KQ5::masterVolume)
+ 0x4a, 0x04, // send 04 - read KQ5::masterVolume
0x36, // push
0x35, 0x04, // ldi 04
0x20, // ge? (followed by bnt)
@@ -296,7 +508,7 @@ const uint16 kq5PatchCdHarpyVolume[] = {
0x38, 0x7b, 0x01, // pushi 017b
0x76, // push0
0x81, 0x01, // lag global[1]
- 0x4a, 0x04, // send 04 (getting KQ5::masterVolume)
+ 0x4a, 0x04, // send 04 - read KQ5::masterVolume
0xa5, 0x03, // sat temp[3] (store volume in temp 3)
// saving 8 bytes due removing of duplicate code
0x39, 0x04, // pushi 04 (saving 1 byte due swapping)
@@ -304,10 +516,10 @@ const uint16 kq5PatchCdHarpyVolume[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature kq5Signatures[] = {
- { 0, "CD: harpy volume change", PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
- { 0, NULL, 0, 0, NULL, NULL }
+ { 0, "CD: harpy volume change", 1, PATCH_MAGICDWORD(0x80, 0x91, 0x01, 0x18), 0, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -355,10 +567,10 @@ const uint16 larry6PatchDeathDialog[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// 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 }
+ { 82, "death dialog memory corruption", 1, PATCH_MAGICDWORD(0x3e, 0x33, 0x01, 0x35), 0, larry6SignatureDeathDialog, larry6PatchDeathDialog },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -367,7 +579,7 @@ const SciScriptSignature larry6Signatures[] = {
// is not in the room. We fix that.
const byte laurabow2SignaturePaintingClosing[] = {
17,
- 0x4a, 0x04, // send 04 (gets aHeimlich::room)
+ 0x4a, 0x04, // send 04 - read aHeimlich::room
0x36, // push
0x81, 0x0b, // lag global[11d] -> current room
0x1c, // ne?
@@ -386,10 +598,110 @@ const uint16 laurabow2PatchPaintingClosing[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature laurabow2Signatures[] = {
- { 560, "painting closing immediately", PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing },
- { 0, NULL, 0, 0, NULL, NULL }
+ { 560, "painting closing immediately", 1, PATCH_MAGICDWORD(0x36, 0x81, 0x0b, 0x1c), -2, laurabow2SignaturePaintingClosing, laurabow2PatchPaintingClosing },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
+// Mother Goose SCI1/SCI1.1
+// MG::replay somewhat calculates the savedgame-id used when saving again
+// this doesn't work right and we remove the code completely.
+// We set the savedgame-id directly right after restoring in kRestoreGame.
+const byte mothergoose256SignatureReplay[] = {
+ 6,
+ 0x36, // push
+ 0x35, 0x20, // ldi 20
+ 0x04, // sub
+ 0xa1, 0xb3, // sag global[b3]
+ 0
+};
+
+const uint16 mothergoose256PatchReplay[] = {
+ 0x34, 0x00, 0x00, // ldi 0000 (dummy)
+ 0x34, 0x00, 0x00, // ldi 0000 (dummy)
+ PATCH_END
+};
+
+// when saving, it also checks if the savegame-id is below 13.
+// we change this to check if below 113 instead
+const byte mothergoose256SignatureSaveLimit[] = {
+ 5,
+ 0x89, 0xb3, // lsg global[b3]
+ 0x35, 0x0d, // ldi 0d
+ 0x20, // ge?
+ 0
+};
+
+const uint16 mothergoose256PatchSaveLimit[] = {
+ PATCH_ADDTOOFFSET | +2,
+ 0x35, 0x0d + SAVEGAMEID_OFFICIALRANGE_START, // ldi 113d
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature mothergoose256Signatures[] = {
+ { 0, "replay save issue", 1, PATCH_MAGICDWORD(0x20, 0x04, 0xa1, 0xb3), -2, mothergoose256SignatureReplay, mothergoose256PatchReplay },
+ { 0, "save limit dialog (SCI1.1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
+ { 994, "save limit dialog (SCI1)", 1, PATCH_MAGICDWORD(0xb3, 0x35, 0x0d, 0x20), -1, mothergoose256SignatureSaveLimit, mothergoose256PatchSaveLimit },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
+// script 215 of qfg1vga pointBox::doit actually processes button-presses
+// during fighting with monsters. It strangely also calls kGetEvent. Because
+// the main User::doit also calls kGetEvent it's pure luck, where the event
+// will hit. It's the same issue as in freddy pharkas and if you turn dos-box
+// to max cycles, sometimes clicks also won't get registered. Strangely it's
+// not nearly as bad as in our sci, but these differences may be caused by
+// timing.
+// We just reuse the active event, thus removing the duplicate kGetEvent call.
+const byte qfg1vgaSignatureFightEvents[] = {
+ 25,
+ 0x39, 0x6d, // pushi 6d (selector new)
+ 0x76, // push0
+ 0x51, 0x07, // class Event
+ 0x4a, 0x04, // send 04 - call Event::new
+ 0xa5, 0x00, // sat temp[0]
+ 0x78, // push1
+ 0x76, // push0
+ 0x4a, 0x04, // send 04 - read Event::x
+ 0xa5, 0x03, // sat temp[3]
+ 0x76, // push0 (selector y)
+ 0x76, // push0
+ 0x85, 0x00, // lat temp[0]
+ 0x4a, 0x04, // send 04 - read Event::y
+ 0x36, // push
+ 0x35, 0x0a, // ldi 0a
+ 0x04, // sub (poor mans localization) ;-)
+ 0
+};
+
+const uint16 qfg1vgaPatchFightEvents[] = {
+ 0x38, 0x5a, 0x01, // pushi 15a (selector curEvent)
+ 0x76, // push0
+ 0x81, 0x50, // lag global[50]
+ 0x4a, 0x04, // send 04 - read User::curEvent -> needs one byte more than previous code
+ 0xa5, 0x00, // sat temp[0]
+ 0x78, // push1
+ 0x76, // push0
+ 0x4a, 0x04, // send 04 - read Event::x
+ 0xa5, 0x03, // sat temp[3]
+ 0x76, // push0 (selector y)
+ 0x76, // push0
+ 0x85, 0x00, // lat temp[0]
+ 0x4a, 0x04, // send 04 - read Event::y
+ 0x39, 0x00, // pushi 00
+ 0x02, // add (waste 3 bytes) - we don't need localization, User::doit has already done it
+ PATCH_END
+};
+
+// script, description, magic DWORD, adjust
+const SciScriptSignature qfg1vgaSignatures[] = {
+ { 215, "fight event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ { 216, "weapon master event issue", 1, PATCH_MAGICDWORD(0x6d, 0x76, 0x51, 0x07), -1, qfg1vgaSignatureFightEvents, qfg1vgaPatchFightEvents },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -415,10 +727,10 @@ const uint16 sq4FloppyPatchEndlessFlight[] = {
PATCH_END
};
-// script, description, magic DWORD, adjust
+// script, description, magic DWORD, adjust
const SciScriptSignature sq4Signatures[] = {
- { 298, "Floppy: endless flight", PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
- { 0, NULL, 0, 0, NULL, NULL }
+ { 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// ===========================================================================
@@ -438,8 +750,8 @@ const byte sq5SignatureScrubbing[] = {
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)
+ 0x4a, 0x04, // send 04 - read ego::mover
+ 0x4a, 0x04, // send 04 - read ego::mover::x
0x36, // push
0x34, 0xa0, 0x00, // ldi 00a0
0x1c, // ne?
@@ -453,35 +765,67 @@ const uint16 sq5PatchScrubbing[] = {
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
0x31, 0x2e, // bnt 2e (jump if ego::mover is 0)
0x78, // push1 (selector x)
0x76, // push0
- 0x4a, 0x04, // send 04 (read ego::mover::x)
+ 0x4a, 0x04, // send 04 - read ego::mover::x
0x39, 0xa0, // pushi a0 (saving 2 bytes)
0x1c, // ne?
PATCH_END
};
-// script, description, magic DWORD, adjust
+// 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 }
+ { 119, "scrubbing send crash", 1, PATCH_MAGICDWORD(0x18, 0x31, 0x37, 0x78), 0, sq5SignatureScrubbing, sq5PatchScrubbing },
+ SCI_SIGNATUREENTRY_TERMINATOR
};
// will actually patch previously found signature area
void Script::applyPatch(const uint16 *patch, byte *scriptData, const uint32 scriptSize, int32 signatureOffset) {
+ byte orgData[PATCH_VALUELIMIT];
int32 offset = signatureOffset;
uint16 patchWord = *patch;
+ // Copy over original bytes from script
+ uint32 orgDataSize = scriptSize - offset;
+ if (orgDataSize > PATCH_VALUELIMIT)
+ orgDataSize = PATCH_VALUELIMIT;
+ memcpy(&orgData, &scriptData[offset], orgDataSize);
+
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;
+ uint16 patchValue = patchWord & PATCH_VALUEMASK;
+ switch (patchWord & PATCH_COMMANDMASK) {
+ case PATCH_ADDTOOFFSET:
+ // add value to offset
+ offset += patchValue & ~PATCH_ADDTOOFFSET;
+ break;
+ case PATCH_GETORIGINALBYTE:
+ // get original byte from script
+ if (patchValue >= orgDataSize)
+ error("patching: can not get requested original byte from script");
+ scriptData[offset] = orgData[patchValue];
+ offset++;
+ break;
+ case PATCH_ADJUSTWORD: {
+ // Adjust word right before current position
+ byte *adjustPtr = &scriptData[offset - 2];
+ uint16 adjustWord = READ_LE_UINT16(adjustPtr);
+ adjustWord += patchValue;
+ WRITE_LE_UINT16(adjustPtr, adjustWord);
+ break;
+ }
+ case PATCH_ADJUSTWORD_NEG: {
+ // Adjust word right before current position (negative way)
+ byte *adjustPtr = &scriptData[offset - 2];
+ uint16 adjustWord = READ_LE_UINT16(adjustPtr);
+ adjustWord -= patchValue;
+ WRITE_LE_UINT16(adjustPtr, adjustWord);
+ break;
+ }
+ default:
+ scriptData[offset] = patchValue & 0xFF;
offset++;
}
patch++;
@@ -499,7 +843,7 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
uint32 DWordOffset = 0;
// first search for the magic DWORD
while (DWordOffset < searchLimit) {
- if (magicDWord == *(const uint32 *)(scriptData + DWordOffset)) {
+ if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
// magic DWORD found, check if actual signature matches
uint32 offset = DWordOffset + signature->magicOffset;
uint32 byteOffset = offset;
@@ -529,33 +873,65 @@ int32 Script::findSignature(const SciScriptSignature *signature, const byte *scr
void Script::matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize) {
const SciScriptSignature *signatureTable = NULL;
- if (g_sci->getGameId() == GID_ECOQUEST)
+ switch (g_sci->getGameId()) {
+ case GID_CASTLEBRAIN:
+ signatureTable = castlebrainSignatures;
+ break;
+ case GID_ECOQUEST:
signatureTable = ecoquest1Signatures;
- if (g_sci->getGameId() == GID_GK1)
+ break;
+ case GID_ECOQUEST2:
+ signatureTable = ecoquest2Signatures;
+ break;
+ case GID_FREDDYPHARKAS:
+ signatureTable = freddypharkasSignatures;
+ break;
+ case GID_GK1:
signatureTable = gk1Signatures;
-// hoyle4 now works due workaround inside GfxPorts
-// if (g_sci->getGameId() == GID_HOYLE4)
-// signatureTable = hoyle4Signatures;
- if (g_sci->getGameId() == GID_KQ5)
+ break;
+ // hoyle4 now works due to workaround inside GfxPorts
+ //case GID_HOYLE4:
+ // signatureTable = hoyle4Signatures;
+ // break;
+ case GID_KQ5:
signatureTable = kq5Signatures;
- if (g_sci->getGameId() == GID_LAURABOW2)
+ break;
+ case GID_LAURABOW2:
signatureTable = laurabow2Signatures;
- if (g_sci->getGameId() == GID_LSL6)
+ break;
+ case GID_LSL6:
signatureTable = larry6Signatures;
- if (g_sci->getGameId() == GID_SQ4)
+ break;
+ case GID_MOTHERGOOSE256:
+ signatureTable = mothergoose256Signatures;
+ break;
+ case GID_QFG1VGA:
+ signatureTable = qfg1vgaSignatures;
+ break;
+ case GID_SQ4:
signatureTable = sq4Signatures;
- if (g_sci->getGameId() == GID_SQ5)
+ break;
+ case GID_SQ5:
signatureTable = sq5Signatures;
+ break;
+ default:
+ break;
+ }
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 and patched %s on script %d offset %d", signatureTable->description, scriptNr, foundOffset);
- applyPatch(signatureTable->patch, scriptData, scriptSize, foundOffset);
- }
+ int32 foundOffset = 0;
+ int16 applyCount = signatureTable->applyCount;
+ do {
+ foundOffset = findSignature(signatureTable, scriptData, scriptSize);
+ if (foundOffset != -1) {
+ // found, so apply the patch
+ warning("matched and patched %s on script %d offset %d", signatureTable->description, scriptNr, foundOffset);
+ applyPatch(signatureTable->patch, scriptData, scriptSize, foundOffset);
+ }
+ applyCount--;
+ } while ((foundOffset != -1) && (applyCount));
}
signatureTable++;
}
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 1fb37f458d..1cbe9a56f4 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -1004,12 +1004,7 @@ int SegManager::instantiateScript(int scriptNum) {
scr->load(_resMan);
scr->initialiseLocals(this);
scr->initialiseClasses(this);
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- scr->initialiseObjectsSci11(this, segmentId);
- } else {
- scr->initialiseObjectsSci0(this, segmentId);
- }
+ scr->initialiseObjects(this, segmentId);
return segmentId;
}
@@ -1052,7 +1047,7 @@ void SegManager::uninstantiateScriptSci0(int script_nr) {
reg_t reg = make_reg(segmentId, oldScriptHeader ? 2 : 0);
int objType, objLength = 0;
- // Make a pass over the object in order uninstantiate all superclasses
+ // Make a pass over the object in order to uninstantiate all superclasses
do {
reg.offset += objLength; // Step over the last checked object
@@ -1074,8 +1069,17 @@ void SegManager::uninstantiateScriptSci0(int script_nr) {
if (superclass_script == script_nr) {
if (scr->getLockers())
scr->decrementLockers(); // Decrease lockers if this is us ourselves
- } else
- uninstantiateScript(superclass_script);
+ } else {
+ if (g_sci->getGameId() == GID_HOYLE3 && (superclass_script == 0 || superclass_script >= 990)) {
+ // HACK for Hoyle 3: when exiting Checkers or Pachisi, scripts 0, 999 and some others
+ // are deleted but are never instantiated again. We ignore deletion of these scripts
+ // here for Hoyle 3 - bug #3038837
+ // TODO/FIXME: find out why this happens, seems like there is a problem with the object
+ // lock code
+ } else {
+ uninstantiateScript(superclass_script);
+ }
+ }
// Recurse to assure that the superclass lockers number gets decreased
}
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index c8cb4cd203..6eca708e2e 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -227,8 +227,12 @@ enum ObjectOffsets {
class Object {
public:
Object() {
- _flags = 0;
_offset = getSciVersion() < SCI_VERSION_1_1 ? 0 : 5;
+ _flags = 0;
+ _baseObj = 0;
+ _baseVars = 0;
+ _baseMethod = 0;
+ _methodCount = 0;
}
~Object() { }
@@ -286,9 +290,6 @@ public:
bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
const Object *getClass(SegManager *segMan) const;
- void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); }
- bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); }
-
void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
bool isFreed() const { return _flags & OBJECT_FLAG_FREED; }
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index f99a41e088..b31f52aa13 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -50,7 +50,7 @@ namespace Sci {
void Kernel::mapSelectors() {
// species
// superClass
- // -info-
+ FIND_SELECTOR2(_info_, "-info-");
FIND_SELECTOR(y);
FIND_SELECTOR(x);
FIND_SELECTOR(view);
@@ -86,7 +86,8 @@ void Kernel::mapSelectors() {
// window
FIND_SELECTOR(cursor);
FIND_SELECTOR(max);
- // mark
+ FIND_SELECTOR(mark);
+ FIND_SELECTOR(sort);
// who
FIND_SELECTOR(message);
// edit
@@ -164,7 +165,6 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(vanishingX);
FIND_SELECTOR(vanishingY);
FIND_SELECTOR(iconIndex);
- FIND_SELECTOR(port);
#ifdef ENABLE_SCI32
FIND_SELECTOR(data);
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 00e795c1b9..98157c3eaf 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -40,6 +40,7 @@ struct SelectorCache {
}
// Statically defined selectors, (almost the) same in all SCI versions
+ Selector _info_;
Selector y;
Selector x;
Selector view, loop, cel; ///< Description of a specific image
@@ -58,7 +59,9 @@ struct SelectorCache {
Selector state, font, type;///< Used by controls
// window
Selector cursor, max; ///< Used by EditControl
- // mark, who
+ Selector mark; //< Used by list controls
+ Selector sort; //< Used by list controls (script internal, is needed by us for QfG3 import room)
+ // who
Selector message; ///< Used by GetEvent
// edit
Selector play; ///< Play function (first function to be called)
@@ -127,8 +130,6 @@ struct SelectorCache {
// 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
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index a069344d61..732f075257 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -110,6 +110,10 @@ void EngineState::reset(bool isRestoring) {
_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
_lastSaveNewId = 0;
+ _chosenQfGImportItem = 0;
+
+ _cursorWorkaroundActive = false;
+
scriptStepCounter = 0;
scriptGCInterval = GC_INTERVAL;
}
@@ -251,7 +255,7 @@ kLanguage SciEngine::getSciLanguage() {
lang = K_LANG_ENGLISH;
if (SELECTOR(printLang) != -1) {
- lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(printLang));
+ lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObjectAddress, 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.
@@ -292,7 +296,7 @@ kLanguage SciEngine::getSciLanguage() {
void SciEngine::setSciLanguage(kLanguage lang) {
if (SELECTOR(printLang) != -1)
- writeSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(printLang), lang);
+ writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(printLang), lang);
}
void SciEngine::setSciLanguage() {
@@ -304,7 +308,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
kLanguage subLang = K_LANG_NONE;
if (SELECTOR(subtitleLang) != -1) {
- subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(subtitleLang));
+ subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(subtitleLang));
}
kLanguage secondLang;
@@ -327,7 +331,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
void SciEngine::checkVocabularySwitch() {
uint16 parserLanguage = 1;
if (SELECTOR(parseLang) != -1)
- parserLanguage = readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(parseLang));
+ parserLanguage = readSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(parseLang));
if (parserLanguage != _vocabularyLanguage) {
delete _vocabulary;
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index 4f1d686b17..d0ddd5ca06 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -59,17 +59,23 @@ enum AbortGameState {
class DirSeeker {
protected:
reg_t _outbuffer;
- Common::StringArray _savefiles;
+ Common::StringArray _files;
+ Common::StringArray _virtualFiles;
Common::StringArray::const_iterator _iter;
public:
DirSeeker() {
_outbuffer = NULL_REG;
- _iter = _savefiles.begin();
+ _iter = _files.begin();
}
reg_t firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan);
reg_t nextFile(SegManager *segMan);
+
+ Common::String getVirtualFilename(uint fileNumber);
+
+private:
+ void addAsVirtualFiles(Common::String title, Common::String fileMask);
};
enum {
@@ -80,11 +86,11 @@ enum {
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
+// We assume that scripts give us savegameId 0->99 for creating a new save slot
+// and savegameId 100->199 for existing save slots ffs. kfile.cpp
enum {
- SAVEGAMEID_OFFICIALRANGE_START = 1000,
- SAVEGAMEID_OFFICIALRANGE_END = 1999
+ SAVEGAMEID_OFFICIALRANGE_START = 100,
+ SAVEGAMEID_OFFICIALRANGE_END = 199
};
enum {
@@ -136,8 +142,14 @@ 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
+ int16 _lastSaveVirtualId; // last virtual id fed to kSaveGame, if no kGetSaveFiles was called inbetween
+ int16 _lastSaveNewId; // last newly created filename-id by kSaveGame
+
+ uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms
+
+ bool _cursorWorkaroundActive; // ffs. GfxCursor::setPosition()
+ Common::Point _cursorWorkaroundPoint;
+ Common::Rect _cursorWorkaroundRect;
public:
/* VM Information */
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index aae6de01f1..ed8d4193a9 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -27,6 +27,7 @@
// them. This includes the King's Quest IV Demo and LSL3 Demo.
#include "sci/engine/kernel.h"
+#include "sci/engine/seg_manager.h"
namespace Sci {
@@ -118,6 +119,7 @@ static const SelectorRemap sciSelectorRemap[] = {
{ 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_1_1, SCI_VERSION_2_1, "-info-",4103 },
{ SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
@@ -133,8 +135,12 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
// Resize the list of selector names and fill in the SCI 0 names.
names.resize(count);
- for (int i = 0; i < offset; i++)
- names[i].clear();
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ // Fill selectors 0 - 2 for SCI0 - SCI1 late
+ names[0] = "species";
+ names[1] = "superClass";
+ names[2] = "-info-";
+ }
if (getSciVersion() <= SCI_VERSION_1_1) {
// SCI0 - SCI11
@@ -149,14 +155,82 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
names[i] = sci1Selectors[i - count];
}
- for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) {
- if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) {
- const uint32 slot = selectorRemap->slot;
- if (slot >= names.size())
- names.resize(slot + 1);
- names[slot] = selectorRemap->name;
+ // Now, we need to find out selectors which keep changing place...
+ // We do that by dissecting game objects, and looking for selectors at
+ // specified locations.
+
+ // We need to initialize script 0 here, to make sure that it's always
+ // located at segment 1.
+ _segMan->instantiateScript(0);
+
+ // The Actor class contains the init, xLast and yLast selectors, which
+ // we reference directly. It's always in script 998, so we need to
+ // explicitly load it here.
+ if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) {
+ _segMan->instantiateScript(998);
+
+ const Object *actorClass = _segMan->getObject(_segMan->findObjectByName("Actor"));
+
+ if (actorClass) {
+ // The init selector is always the first function
+ int initSelectorPos = actorClass->getFuncSelector(0);
+
+ if (names.size() < (uint32)initSelectorPos + 2)
+ names.resize((uint32)initSelectorPos + 2);
+
+ names[initSelectorPos] = "init";
+ // dispose comes right after init
+ names[initSelectorPos + 1] = "dispose";
+
+ if ((getSciVersion() >= SCI_VERSION_1_EGA)) {
+ // Find the xLast and yLast selectors, used in kDoBresen
+
+ // xLast and yLast always come between illegalBits and xStep
+ int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset); // illegalBits
+ int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset); // xStep
+ if (xStepSelectorPos - illegalBitsSelectorPos != 3) {
+ error("illegalBits and xStep selectors aren't found in "
+ "known locations. illegalBits = %d, xStep = %d",
+ illegalBitsSelectorPos, xStepSelectorPos);
+ }
+
+ int xLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 1);
+ int yLastSelectorPos = actorClass->getVarSelector(illegalBitsSelectorPos + 2);
+
+ if (names.size() < (uint32)yLastSelectorPos + 1)
+ names.resize((uint32)yLastSelectorPos + 1);
+
+ names[xLastSelectorPos] = "xLast";
+ names[yLastSelectorPos] = "yLast";
+ } // if ((getSciVersion() >= SCI_VERSION_1_EGA))
+ } // if (actorClass)
+
+ _segMan->uninstantiateScript(998);
+ } // if (_resMan->testResource(ResourceId(kResourceTypeScript, 998)))
+
+ if (_resMan->testResource(ResourceId(kResourceTypeScript, 981))) {
+ // The SysWindow class contains the open selectors, which we
+ // reference directly. It's always in script 981, so we need to
+ // explicitly load it here
+ _segMan->instantiateScript(981);
+
+ const Object *sysWindowClass = _segMan->getObject(_segMan->findObjectByName("SysWindow"));
+
+ if (sysWindowClass) {
+ if (sysWindowClass->getMethodCount() < 2)
+ error("The SysWindow class has less than 2 methods");
+
+ // The open selector is always the second function
+ int openSelectorPos = sysWindowClass->getFuncSelector(1);
+
+ if (names.size() < (uint32)openSelectorPos + 1)
+ names.resize((uint32)openSelectorPos + 1);
+
+ names[openSelectorPos] = "open";
}
- }
+
+ _segMan->uninstantiateScript(981);
+ } // if (_resMan->testResource(ResourceId(kResourceTypeScript, 981)))
if (g_sci->getGameId() == GID_HOYLE4) {
// The demo of Hoyle 4 is one of the few demos with lip syncing and no selector vocabulary.
@@ -168,21 +242,26 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
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);
+ } else if (g_sci->getGameId() == GID_PEPPER) {
+ // Same as above for the non-interactive demo of Pepper
+ if (names.size() < 539)
+ names.resize(539);
- names[110] = "init";
+ names[263] = "syncTime";
+ names[264] = "syncCue";
+ names[538] = "startText";
} 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);
+ // The floppy of version needs the changeState selector set to match up with the
+ // CD version's workarounds.
+ if (names.size() < 251)
+ names.resize(251);
names[144] = "changeState";
- names[189] = "open";
+ } else if (g_sci->getGameId() == GID_CNICK_KQ) {
+ if (names.size() < 447)
+ names.resize(447);
+
+ names[446] = "say";
}
#ifdef ENABLE_SCI32
@@ -193,6 +272,15 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
#endif
}
+ for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) {
+ if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) {
+ const uint32 slot = selectorRemap->slot;
+ if (slot >= names.size())
+ names.resize(slot + 1);
+ names[slot] = selectorRemap->name;
+ }
+ }
+
return names;
}
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 0500cc601b..7342f8ca7b 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -207,10 +207,21 @@ static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, i
// We need to find correct replacements for each situation manually
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply);
- if (solution.type == WORKAROUND_NONE)
+ if (solution.type == WORKAROUND_NONE) {
+#ifdef RELEASE_BUILD
+ // If we are running an official ScummVM release -> fake 0 in unknown cases
+ warning("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);
+
+ r[index] = NULL_REG;
+ break;
+#else
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);
+#endif
+ }
assert(solution.type == WORKAROUND_FAKE);
r[index] = make_reg(0, solution.value);
break;
@@ -295,7 +306,7 @@ bool SciEngine::checkExportBreakpoint(uint16 script, uint16 pubfunct) {
_console->DebugPrintf("Break on script %d, export %d\n", script, pubfunct);
_debugState.debugging = true;
_debugState.breakpointWasHit = true;
- return true;;
+ return true;
}
}
}
@@ -318,10 +329,10 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
// HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has
// an invalid exported function.
if (getSciVersion() >= SCI_VERSION_2)
- warning("Request for invalid exported function 0x%x of script 0x%x", pubfunct, script);
+ warning("Request for invalid exported function 0x%x of script %d", pubfunct, script);
else
#endif
- error("Request for invalid exported function 0x%x of script 0x%x", pubfunct, script);
+ error("Request for invalid exported function 0x%x of script %d", pubfunct, script);
return NULL;
}
@@ -366,27 +377,24 @@ 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];
+bool SciEngine::checkSelectorBreakpoint(BreakpointType breakpointType, reg_t send_obj, int selector) {
+ char method_name[256];
- sprintf(method_name, "%s::%s", _gamestate->_segMan->getObjectName(send_obj), getKernel()->getSelectorName(selector).c_str());
+ 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;
+ 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;
- }
+ if (bp->type == breakpointType && !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;
}
@@ -399,12 +407,13 @@ 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
- bool printSendActions = false;
// We return a pointer to the new active ExecStack
// The selector calls we catch are stored below:
Common::Stack<CallsStruct> sendCalls;
+ int activeBreakpointTypes = g_sci->_debugState._activeBreakpointTypes;
+
while (framesize > 0) {
selector = validate_arithmetic(*argp++);
argc = validate_arithmetic(*argp);
@@ -413,9 +422,6 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
error("send_selector(): More than 0x800 arguments to function call");
}
- // Check if a breakpoint is set on this method
- printSendActions = g_sci->checkSelectorBreakpoint(send_obj, selector);
-
#ifdef VM_DEBUG_SEND
printf("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
s->_segMan->getObjectName(send_obj), selector,
@@ -439,18 +445,23 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
// argc == 0: read selector
// argc != 0: write selector
- if (printSendActions && !argc) { // read selector
- debug("[read selector]\n");
- printSendActions = false;
- }
-
- if (printSendActions && argc) {
- reg_t oldReg = *varp.getPointer(s->_segMan);
- reg_t newReg = argp[1];
- 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) {
+ // read selector
+ if (activeBreakpointTypes & BREAK_SELECTORREAD) {
+ if (g_sci->checkSelectorBreakpoint(BREAK_SELECTORREAD, send_obj, selector))
+ debug("[read selector]\n");
+ }
+ } else {
+ // write selector
+ if (activeBreakpointTypes & BREAK_SELECTORWRITE) {
+ if (g_sci->checkSelectorBreakpoint(BREAK_SELECTORWRITE, send_obj, selector)) {
+ reg_t oldReg = *varp.getPointer(s->_segMan);
+ reg_t newReg = argp[1];
+ 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));
+ }
+ }
}
if (argc > 1) {
@@ -482,7 +493,35 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
case kSelectorMethod:
-#ifdef VM_DEBUG_SEND
+#ifndef VM_DEBUG_SEND
+ if (activeBreakpointTypes & BREAK_SELECTOREXEC) {
+ if (g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector)) {
+ printf("[execute selector]");
+
+ 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;
+ }
+ }
+ printf("\n");
+ }
+ }
+#else // VM_DEBUG_SEND
+ if (activeBreakpointTypes & BREAK_SELECTOREXEC)
+ g_sci->checkSelectorBreakpoint(BREAK_SELECTOREXEC, send_obj, selector);
printf("Funcselector(");
for (int i = 0; i < argc; i++) {
printf("%04x:%04x", PRINT_REG(argp[i+1]));
@@ -491,31 +530,6 @@ 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 (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;
- }
{
CallsStruct call;
@@ -817,7 +831,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
for (int i = 0; g_opcode_formats[opcode][i]; ++i) {
//printf("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
- assert(i < 4);
+ assert(i < 3);
switch (g_opcode_formats[opcode][i]) {
case Script_Byte:
@@ -911,7 +925,7 @@ void run_vm(EngineState *s) {
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())
+ if (s->abortScriptProcessing != kAbortNone)
return; // Stop processing
if (s->_executionStackPosChanged) {
@@ -942,7 +956,7 @@ void run_vm(EngineState *s) {
s->variables[VAR_PARAM] = s->xs->variables_argp;
}
- if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ if (s->abortScriptProcessing != kAbortNone)
return; // Stop processing
// Debug if this has been requested:
@@ -1319,7 +1333,7 @@ void run_vm(EngineState *s) {
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);
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeUltWorkarounds, r_temp, s->r_acc);
}
break;
@@ -1442,7 +1456,7 @@ void run_vm(EngineState *s) {
s->_executionStackPosChanged = true;
// If a game is being loaded, stop processing
- if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ if (s->abortScriptProcessing != kAbortNone)
return; // Stop processing
break;
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 95674ceaad..d332c64a9d 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -48,6 +48,8 @@ const SciWorkaroundEntry opcodeDptoaWorkarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry opcodeGeWorkarounds[] = {
+ { GID_HOYLE1, 5, 213, 0, "", "export 0", -1, 0, { WORKAROUND_FAKE, 1 } }, // happens sometimes during cribbage - bug #3038433
+ { GID_MOTHERGOOSE256, 4, 998, 0, "door", "setCel", -1, 0, { WORKAROUND_FAKE, 1 } }, // after giving the king his pipe back, listening to his song and leaving the castle - bug #3051475
{ 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
};
@@ -59,6 +61,12 @@ const SciWorkaroundEntry opcodeLeWorkarounds[] = {
};
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeUltWorkarounds[] = {
+ { GID_HOYLE3, 400, 0, 1, "Character", "say", -1, 0, { WORKAROUND_FAKE, 0 } }, // While playing Pachisi, when any character starts to talk - bug #3038837
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry opcodeLaiWorkarounds[] = {
{ GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", 0x20d, 0, { WORKAROUND_FAKE, 0 } }, // during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #3044734
SCI_WORKAROUNDENTRY_TERMINATOR
@@ -78,37 +86,38 @@ const SciWorkaroundEntry opcodeMulWorkarounds[] = {
// 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
- // ^^ TODO: which of the mother goose versions is affected by this? EGA? SCI1? SCI1.1?
+ { GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // constantly during the game
+ // ^^ TODO: which of the mother goose versions is affected by this? EGA? SCI1? SCI1.1?
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
+ { 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
+ { GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going north and reaching the castle (rooms 4 and 37) - bug #3038228
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_CNICK_KQ, 200, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3 - temps 504 and 505
+ { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu, like in hoyle 3
+ { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "init", -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_FANMADE, 488, 1, 0, "RoomScript", "doit", 0x1f17, 1, { WORKAROUND_FAKE, 0 } }, // Happens in Ocean Battle while playing - bug #3059871
{ 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_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", -1, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game (bug #3044218)
+ { GID_FREDDYPHARKAS, 540, 540, 0, "WaverCode", "init", -1, -1, { WORKAROUND_FAKE, 0 } }, // Gun pratice mini-game (bug #3044218)
{ 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_HOYLE1, 3, 16, 0, "", "export 0", 0x37c, 3, { WORKAROUND_FAKE, 0 } }, // Hearts / during the game - bug #3052359
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, -1, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something - temps 504 and 505
{ GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
{ 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, 18, 0, "Tray", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // during tutorial - bug #3042756
@@ -117,19 +126,18 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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_JONES, 1, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends, temps 13 and 14
+ { GID_JONES, 764, 255, 0, "", "export 0", -1, -1, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts, temps 13 and 14
//{ GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0xf } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #3034700
- // ^^ shouldn't be needed anymore, we got a script patch instead (kq5PatchCdHarpyVolume)
+ // ^^ shouldn't be needed anymore, we got a script patch instead (kq5PatchCdHarpyVolume)
{ 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_KQ5, 55, 55, 0, "helpScript", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #3041262
+ { GID_KQ5, 55, 55, 0, "helpScript", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #3041262
{ 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_KQ6, -1, 907, 0, "tomato", "doVerb", -1, 2, { WORKAROUND_FAKE, 0 } }, // when looking at the rotten tomato in the inventory - bug #3059544
{ 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)
@@ -137,6 +145,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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, 1, "MuseumActor", "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, 0, 0, "Longbow", "restart", -1, 0, { WORKAROUND_FAKE, 0 } }, // When canceling a restart game - bug #3046200
{ 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
@@ -150,23 +159,29 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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, -1, 0, 0, "MG", "doit", -1, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game (bug #3043955)
- { 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_MOTHERGOOSE256, -1, 0, 0, "MG", "doit", -1, 5, { WORKAROUND_FAKE, 0 } }, // SCI1.1: When moving the cursor all the way to the left during the game (bug #3043955)
+ { GID_MOTHERGOOSE256, -1, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // Happens in the demo and full version. In the demo, it happens when walking two screens from mother goose's house to the north. In the full version, it happens in rooms 7 and 23 - bug #3049146
+ { GID_MOTHERGOOSE256, 94, 94, 0, "sunrise", "changeState", -1, 367, { WORKAROUND_FAKE, 0 } }, // At the very end, after the game is completed - bug #3051163
{ 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_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
{ 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_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #3053268
{ 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, 510, 510, 0, "awardPrize", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Simbani warrior challenge, after throwing the spears and retrieving the ring - bug #3049435
{ 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_QFG3, 490, 490, -1, "computersMove", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // when finishing awari game, bug #3040579
+ { GID_QFG3, 490, 490, -1, "computersMove", "changeState", 0xf53, 4, { WORKAROUND_FAKE, 0 } }, // also when finishing awari game
+ { GID_QFG3, 851, 32, -1, "ProjObj", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // near the end, when throwing the spear of death, bug #3050122
{ 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
@@ -176,7 +191,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin
{ GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: 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, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100)
{ 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
@@ -196,6 +211,7 @@ 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
+ { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #3039679
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -204,6 +220,7 @@ 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
+ { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #3039679
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -227,13 +244,18 @@ const SciWorkaroundEntry kDisplay_workarounds[] = {
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4c1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id (another pq2 version, bug #3043904)
{ GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
{ GID_SQ1, -1, 700, 0, "arcadaRegion", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restoring in some rooms of the arcada (right at the start)
- { GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store (bug #3044044)
+ { GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store (bug #3044044)
{ GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
{ GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: 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 kDirLoop_workarounds[] = {
+ { GID_KQ4, 4, 992, 0, "Avoid", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when the ogre catches you in front of his house, second parameter points to the same object as the first parameter, instead of being an integer (the angle) - bug #3042964
+};
+
+// 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
@@ -292,6 +314,7 @@ const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
{ 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
+ { GID_SQ5, 850, 850, 0, "quirksTurn", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -344,7 +367,7 @@ const SciWorkaroundEntry kMemory_workarounds[] = {
// 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
+ { GID_ECOQUEST, -1, 981, 0, "SysWindow", "open", -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
};
@@ -375,6 +398,12 @@ const SciWorkaroundEntry kStrAt_workarounds[] = {
};
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kStrCat_workarounds[] = {
+ { GID_LONGBOW, 210, 210, 0, "giveScroll", "changeState",0x3294, 0, { WORKAROUND_FAKE, 0 } }, // German version, when handing the scroll with the druid hand code to Marion - bug #3048054
+ 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
@@ -391,6 +420,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = {
{ 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
+ { GID_SQ3, 2, 998, 0, "View", "delete", -1, 0, { WORKAROUND_IGNORE, 0 } }, // clicking the mouse button during the intro, after the escape pod gets pulled into the garbage freighter, the reference is invalid - bug #3050856
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index 55a4b8f885..bf1ac3a445 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -72,6 +72,7 @@ extern const SciWorkaroundEntry opcodeDivWorkarounds[];
extern const SciWorkaroundEntry opcodeDptoaWorkarounds[];
extern const SciWorkaroundEntry opcodeGeWorkarounds[];
extern const SciWorkaroundEntry opcodeLeWorkarounds[];
+extern const SciWorkaroundEntry opcodeUltWorkarounds[];
extern const SciWorkaroundEntry opcodeLaiWorkarounds[];
extern const SciWorkaroundEntry opcodeLsiWorkarounds[];
extern const SciWorkaroundEntry opcodeMulWorkarounds[];
@@ -83,6 +84,7 @@ extern const SciWorkaroundEntry kCelHigh_workarounds[];
extern const SciWorkaroundEntry kCelWide_workarounds[];
extern const SciWorkaroundEntry kDeviceInfo_workarounds[];
extern const SciWorkaroundEntry kDisplay_workarounds[];
+extern const SciWorkaroundEntry kDirLoop_workarounds[];
extern const SciWorkaroundEntry kDisposeScript_workarounds[];
extern const SciWorkaroundEntry kDoSoundFade_workarounds[];
extern const SciWorkaroundEntry kFindKey_workarounds[];
@@ -101,6 +103,7 @@ extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[];
extern const SciWorkaroundEntry kSetCursor_workarounds[];
extern const SciWorkaroundEntry kSetPort_workarounds[];
extern const SciWorkaroundEntry kStrAt_workarounds[];
+extern const SciWorkaroundEntry kStrCat_workarounds[];
extern const SciWorkaroundEntry kUnLoad_workarounds[];
extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin);
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 5923e501cf..5d469eda7b 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -149,7 +149,7 @@ SciEvent EventManager::getScummVMEvent() {
found = em->pollEvent(ev);
}
- if (found && !ev.synthetic && ev.type != Common::EVENT_MOUSEMOVE) {
+ if (found && ev.type != Common::EVENT_MOUSEMOVE) {
int modifiers = em->getModifierState();
// We add the modifier key status to buckybits
@@ -215,6 +215,11 @@ SciEvent EventManager::getScummVMEvent() {
else
input.character = SCI_KEY_TAB;
}
+ if (input.data == Common::KEYCODE_DELETE) {
+ // Delete key
+ input.type = SCI_EVENT_KEYBOARD;
+ input.data = input.character = SCI_KEY_DELETE;
+ }
} else if ((input.data >= Common::KEYCODE_F1) && input.data <= Common::KEYCODE_F10) {
// F1-F10
input.type = SCI_EVENT_KEYBOARD;
@@ -346,9 +351,17 @@ SciEvent EventManager::getScummVMEvent() {
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) {
+ EngineState *s = g_sci->getEngineState();
+ if (g_system->getMillis() - s->_screenUpdateTime >= 1000 / 60) {
g_system->updateScreen();
- g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ s->_screenUpdateTime = g_system->getMillis();
+ // Throttle the checking of shouldQuit() to 60fps as well, since
+ // Engine::shouldQuit() invokes 2 virtual functions
+ // (EventManager::shouldQuit() and EventManager::shouldRTL()),
+ // which is very expensive to invoke constantly without any
+ // throttling at all.
+ if (g_engine->shouldQuit())
+ s->abortScriptProcessing = kAbortQuitGame;
}
}
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index b962e819a6..62c5f9c19e 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -96,7 +96,7 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
invokeSelector(_s, curObject, SELECTOR(doit), argc, argv, 0);
// If a game is being loaded, stop processing
- if (_s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ if (_s->abortScriptProcessing != kAbortNone)
return true; // Stop processing
// Lookup node again, since the nodetable it was in may have been reallocated.
@@ -141,6 +141,7 @@ void GfxAnimate::makeSortedList(List *list) {
AnimateEntry listEntry;
const reg_t curObject = curNode->value;
listEntry.object = curObject;
+ listEntry.castHandle = NULL_REG;
// Get data from current object
listEntry.givenOrderNo = listNr;
@@ -190,6 +191,34 @@ void GfxAnimate::makeSortedList(List *list) {
Common::sort(_list.begin(), _list.end(), sortHelper);
}
+void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) {
+ reg_t curObject = entry->object;
+
+ // 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(entry->loopNo, entry->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 = entry->y - vanishingY;
+ if (!fixedEntryY)
+ fixedEntryY = 1;
+
+ if ((celHeight == 0) || (fixedPortY == 0))
+ error("global scaling panic");
+
+ entry->scaleY = ( maxCelHeight * fixedEntryY ) / fixedPortY;
+ entry->scaleY = (entry->scaleY * 128) / celHeight;
+
+ entry->scaleX = entry->scaleY;
+
+ // and set objects scale selectors
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), entry->scaleX);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY);
+}
+
void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
reg_t curObject;
uint16 signal;
@@ -229,44 +258,22 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
it->celNo = viewCelCount - 1;
}
- // 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);
- }
- }
-
- //warning("%s view %d, loop %d, cel %d", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->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;
+ } else {
+ // Process global scaling, if needed
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ applyGlobalScaling(it, view);
+ }
+ }
}
+ //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal);
+
bool setNsRect = maySetNsRect;
// Create rect according to coordinates and given cel
@@ -278,6 +285,16 @@ void GfxAnimate::fill(byte &old_picNotValid, bool maySetNsRect) {
} else {
view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
}
+
+ // This statement must be here for Hoyle4, otherwise cards are unclickable.
+ // This is probably one of the experimental features that were occasionally
+ // added to SCI interpreters; the corresponding check is absent in many SSCI
+ // versions. m_kiewitz knew about this flag before I (lskovlun) implemented it,
+ // so it is possible that more test cases are known. Also, some presently open
+ // SCI1.1 bugs may be fixed by this and should be re-tested with this patch generalized.
+ if (it->scaleSignal & kScaleSignalDontSetNsrect)
+ setNsRect = false;
+
if (setNsRect) {
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
@@ -533,19 +550,6 @@ void GfxAnimate::reAnimate(Common::Rect rect) {
}
}
-void GfxAnimate::preprocessAddToPicList() {
- AnimateList::iterator it;
- const AnimateList::iterator end = _list.end();
-
- for (it = _list.begin(); it != end; ++it) {
- if (it->priority == -1)
- it->priority = _ports->kernelCoordinateToPriority(it->y);
-
- // Do not allow priority to get changed by fill()
- it->signal |= kSignalFixedPriority;
- }
-}
-
void GfxAnimate::addToPicDrawCels() {
reg_t curObject;
GfxView *view = NULL;
@@ -558,8 +562,31 @@ void GfxAnimate::addToPicDrawCels() {
// Get the corresponding view
view = _cache->getView(it->viewId);
+ // kAddToPic does not do loop/cel-number fixups, it also doesn't support global scaling
+
+ if (it->priority == -1)
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
+
+ if (!view->isScaleable()) {
+ // Laura Bow 2 specific - ffs. fill()
+ it->scaleSignal = 0;
+ it->scaleY = it->scaleX = 128;
+ }
+
+ // Create rect according to coordinates and given cel
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ applyGlobalScaling(it, view);
+ view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
+ 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);
+ } else {
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
+ }
+
// draw corresponding cel
- _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ _paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
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);
@@ -677,6 +704,7 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
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 == 5)) || // Freddy Pharkas "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);
@@ -703,7 +731,6 @@ void GfxAnimate::addToPicSetPicNotValid() {
void GfxAnimate::kernelAddToPicList(reg_t listReference, int argc, reg_t *argv) {
List *list;
- byte tempPicNotValid = 0;
_ports->setPort((Port *)_ports->_picWind);
@@ -712,8 +739,6 @@ void GfxAnimate::kernelAddToPicList(reg_t listReference, int argc, reg_t *argv)
error("kAddToPic called with non-list as parameter");
makeSortedList(list);
- preprocessAddToPicList();
- fill(tempPicNotValid, getSciVersion() >= SCI_VERSION_1_1 ? true : false);
addToPicDrawCels();
addToPicSetPicNotValid();
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index f25e54915e..23e7a624d8 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -53,7 +53,8 @@ enum ViewSignals {
enum ViewScaleSignals {
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
+ kScaleSignalDontSetNsrect = 0x0004 // do not set nsRect inside kAnimate(); for a test case see bug #3038424
+
};
struct AnimateEntry {
@@ -83,6 +84,7 @@ class GfxPaint16;
class GfxScreen;
class GfxPalette;
class GfxTransitions;
+class GfxView;
/**
* Animate class, kAnimate and relevant functions for SCI16 (SCI0-SCI1.1) games
*/
@@ -94,13 +96,13 @@ public:
void disposeLastCast();
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
+ void applyGlobalScaling(AnimateList::iterator entry, GfxView *view);
void fill(byte &oldPicNotValid, bool maySetNsRect);
void update();
void drawCels();
void updateScreen(byte oldPicNotValid);
void restoreAndDelete(int argc, reg_t *argv);
void reAnimate(Common::Rect rect);
- void preprocessAddToPicList();
void addToPicDrawCels();
void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 1c961b2ad6..6a99d2384e 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -185,9 +185,9 @@ reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
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
+ if (!checkRect.isValidRect()) { // can occur in Iceman and Mother Goose - 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 NULL_REG;
+ return NULL_REG; // this means "can be here"
}
adjustedRect = _coordAdjuster->onControl(checkRect);
@@ -237,20 +237,24 @@ void GfxCompare::kernelBaseSetter(reg_t object) {
Common::Rect celRect;
GfxView *tmpView = _cache->getView(viewId);
- if (tmpView->isSci2Hires())
- _screen->adjustToUpscaledCoordinates(y, x);
+ if (!tmpView->isScaleable())
+ scaleSignal = 0;
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);
+ celRect.left = readSelectorValue(_segMan, object, SELECTOR(nsLeft));
+ celRect.right = readSelectorValue(_segMan, object, SELECTOR(nsRight));
+ celRect.top = readSelectorValue(_segMan, object, SELECTOR(nsTop));
+ celRect.bottom = readSelectorValue(_segMan, object, SELECTOR(nsBottom));
} else {
+ if (tmpView->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(y, x);
+
tmpView->getCelRect(loopNo, celNo, x, y, z, celRect);
- }
- if (tmpView->isSci2Hires()) {
- _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
- _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ if (tmpView->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ }
}
celRect.bottom = y + 1;
diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index 5891413be8..66376a793c 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -150,7 +150,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
Common::String text;
- uint16 textSize, eventType, eventKey = 0;
+ uint16 textSize, eventType, eventKey = 0, modifiers = 0;
bool textChanged = false;
bool textAddChar = false;
Common::Rect rect;
@@ -159,6 +159,8 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
error("kEditControl called on object that doesnt have a text reference");
text = _segMan->getString(textReference);
+ uint16 oldCursorPos = cursorPos;
+
if (!eventObject.isNull()) {
textSize = text.size();
eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
@@ -169,6 +171,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
break;
case SCI_EVENT_KEYBOARD:
eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message));
+ modifiers = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers));
switch (eventKey) {
case SCI_KEY_BACKSPACE:
if (cursorPos > 0) {
@@ -177,8 +180,10 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
}
break;
case SCI_KEY_DELETE:
- text.deleteChar(cursorPos);
- textChanged = true;
+ if (cursorPos < textSize) {
+ text.deleteChar(cursorPos);
+ textChanged = true;
+ }
break;
case SCI_KEY_HOME: // HOME
cursorPos = 0; textChanged = true;
@@ -196,8 +201,20 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
cursorPos++; textChanged = true;
}
break;
+ case 3: // returned in SCI1 late and newer when Control - C is pressed
+ if (modifiers & SCI_KEYMOD_CTRL) {
+ // Control-C erases the whole line
+ cursorPos = 0; text.clear();
+ textChanged = true;
+ }
+ break;
default:
- if (eventKey > 31 && eventKey < 256 && textSize < maxChars) {
+ if ((modifiers & SCI_KEYMOD_CTRL) && eventKey == 99) {
+ // Control-C in earlier SCI games (SCI0 - SCI1 middle)
+ // Control-C erases the whole line
+ cursorPos = 0; text.clear();
+ textChanged = true;
+ } else if (eventKey > 31 && eventKey < 256 && textSize < maxChars) {
// insert pressed character
textAddChar = true;
textChanged = true;
@@ -208,6 +225,11 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
}
}
+ if (g_sci->getVocabulary() && !textChanged && oldCursorPos != cursorPos) {
+ assert(!textAddChar);
+ textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos);
+ }
+
if (textChanged) {
GuiResourceId oldFontId = _text16->GetFontId();
GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
@@ -215,18 +237,28 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
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
- uint16 textWidth = 0;
+
const char *textPtr = text.c_str();
+
+ // We check if we are really able to add the new char
+ uint16 textWidth = 0;
while (*textPtr)
- textWidth += _text16->_font->getCharWidth(*textPtr++);
+ textWidth += _text16->_font->getCharWidth((byte)*textPtr++);
textWidth += _text16->_font->getCharWidth(eventKey);
+
+ // Does it fit?
if (textWidth >= rect.width()) {
_text16->SetFont(oldFontId);
return;
}
+
text.insertChar(eventKey, cursorPos++);
+
+ // Note: the following checkAltInput call might make the text
+ // too wide to fit, but SSCI fails to check that too.
}
+ if (g_sci->getVocabulary())
+ g_sci->getVocabulary()->checkAltInput(text, cursorPos);
texteditCursorErase();
_paint16->eraseRect(rect);
_text16->Box(text.c_str(), 0, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1);
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index a906899113..7a37d7e865 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -49,10 +49,21 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
// center mouse cursor
setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2));
_moveZoneActive = false;
+
+ _zoomZoneActive = false;
+ _zoomZone = Common::Rect();
+ _zoomCursorView = 0;
+ _zoomCursorLoop = 0;
+ _zoomCursorCel = 0;
+ _zoomPicView = 0;
+ _zoomColor = 0;
+ _zoomMultiplier = 0;
+ _cursorSurface = 0;
}
GfxCursor::~GfxCursor() {
purgeCache();
+ kernelClearZoomZone();
}
void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) {
@@ -266,6 +277,16 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
kernelShow();
}
+// this list contains all mandatory set cursor changes, that need special handling
+// ffs. GfxCursor::setPosition (below)
+// Game, newPosition, validRect
+static const SciCursorSetPositionWorkarounds setPositionWorkarounds[] = {
+ { GID_ISLANDBRAIN, 84, 109, 46, 76, 174, 243 }, // island of dr. brain / game menu
+ { GID_LSL5, 23, 171, 0, 0, 26, 320 }, // larry 5 / skip forward helper
+ { GID_QFG1VGA, 64, 174, 40, 37, 74, 284 }, // Quest For Glory 1 VGA / run/walk/sleep sub-menu
+ { (SciGameId)0, -1, -1, -1, -1, -1, -1 }
+};
+
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
@@ -282,6 +303,31 @@ void GfxCursor::setPosition(Common::Point pos) {
_screen->adjustToUpscaledCoordinates(pos.y, pos.x);
g_system->warpMouse(pos.x, pos.y);
}
+
+ // Some games display a new menu, set mouse position somewhere within and
+ // expect it to be in there. This is fine for a real mouse, but on wii using
+ // wii-mote or touch interfaces this won't work. In fact on those platforms
+ // the menus will close immediately because of that behaviour.
+ // We identify those cases and set a reaction-rect. If the mouse it outside
+ // of that rect, we won't report the position back to the scripts.
+ // As soon as the mouse was inside once, we will revert to normal behaviour
+ // Currently this code is enabled for all platforms, especially because we can't
+ // differentiate between e.g. Windows used via mouse and Windows used via touchscreen
+ // The workaround won't hurt real-mouse platforms
+ const SciGameId gameId = g_sci->getGameId();
+ const SciCursorSetPositionWorkarounds *workaround;
+ workaround = setPositionWorkarounds;
+ while (workaround->newPositionX != -1) {
+ if (workaround->gameId == gameId
+ && ((workaround->newPositionX == pos.x) && (workaround->newPositionY == pos.y))) {
+ EngineState *s = g_sci->getEngineState();
+ s->_cursorWorkaroundActive = true;
+ s->_cursorWorkaroundPoint = pos;
+ s->_cursorWorkaroundRect = Common::Rect(workaround->rectLeft, workaround->rectTop, workaround->rectRight, workaround->rectBottom);
+ return;
+ }
+ workaround++;
+ }
}
Common::Point GfxCursor::getPosition() {
@@ -294,9 +340,10 @@ Common::Point GfxCursor::getPosition() {
}
void GfxCursor::refreshPosition() {
+ Common::Point mousePoint = getPosition();
+
if (_moveZoneActive) {
bool clipped = false;
- Common::Point mousePoint = getPosition();
if (mousePoint.x < _moveZone.left) {
mousePoint.x = _moveZone.left;
@@ -318,6 +365,52 @@ void GfxCursor::refreshPosition() {
if (clipped)
setPosition(mousePoint);
}
+
+ if (_zoomZoneActive) {
+ // Cursor
+ const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel);
+ const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel);
+ // Pic
+ const CelInfo *picCelInfo = _zoomPicView->getCelInfo(0, 0);
+ const byte *rawPicBitmap = _zoomPicView->getBitmap(0, 0);
+
+ // Compute hotspot of cursor
+ Common::Point cursorHotspot = Common::Point((cursorCelInfo->width >> 1) - cursorCelInfo->displaceX, cursorCelInfo->height - cursorCelInfo->displaceY - 1);
+
+ int16 targetX = ((mousePoint.x - _moveZone.left) * _zoomMultiplier);
+ int16 targetY = ((mousePoint.y - _moveZone.top) * _zoomMultiplier);
+ if (targetX < 0)
+ targetX = 0;
+ if (targetY < 0)
+ targetY = 0;
+
+ targetX -= cursorHotspot.x;
+ targetY -= cursorHotspot.y;
+
+ // Sierra SCI actually drew only within zoom area, thus removing the need to fill any other pixels with upmost/left
+ // color of the picture cel. This also made the cursor not appear on top of everything. They actually drew the
+ // cursor manually within kAnimate processing and used a hidden cursor for moving.
+ // TODO: we should also do this
+
+ // Replace the special magnifier color with the associated magnified pixels
+ for (int x = 0; x < cursorCelInfo->width; x++) {
+ for (int y = 0; y < cursorCelInfo->height; y++) {
+ int curPos = cursorCelInfo->width * y + x;
+ if (cursorBitmap[curPos] == _zoomColor) {
+ int16 rawY = targetY + y;
+ int16 rawX = targetX + x;
+ if ((rawY >= 0) && (rawY < picCelInfo->height) && (rawX >= 0) && (rawX < picCelInfo->width)) {
+ int rawPos = picCelInfo->width * rawY + rawX;
+ _cursorSurface[curPos] = rawPicBitmap[rawPos];
+ } else {
+ _cursorSurface[curPos] = rawPicBitmap[0]; // use left and upmost pixel color
+ }
+ }
+ }
+ }
+
+ CursorMan.replaceCursor((const byte *)_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
+ }
}
void GfxCursor::kernelResetMoveZone() {
@@ -329,6 +422,44 @@ void GfxCursor::kernelSetMoveZone(Common::Rect zone) {
_moveZoneActive = true;
}
+void GfxCursor::kernelClearZoomZone() {
+ kernelResetMoveZone();
+ _zoomZone = Common::Rect();
+ _zoomColor = 0;
+ _zoomMultiplier = 0;
+ _zoomZoneActive = false;
+ delete _zoomCursorView;
+ _zoomCursorView = 0;
+ delete _zoomPicView;
+ _zoomPicView = 0;
+ delete[] _cursorSurface;
+ _cursorSurface = 0;
+}
+
+void GfxCursor::kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor) {
+ kernelClearZoomZone();
+
+ _zoomMultiplier = multiplier;
+
+ if (_zoomMultiplier != 1 && _zoomMultiplier != 2 && _zoomMultiplier != 4)
+ error("Unexpected zoom multiplier (expected 1, 2 or 4)");
+
+ _zoomCursorView = new GfxView(_resMan, _screen, _palette, viewNum);
+ _zoomCursorLoop = (byte)loopNum;
+ _zoomCursorCel = (byte)celNum;
+ _zoomPicView = new GfxView(_resMan, _screen, _palette, picNum);
+ const CelInfo *cursorCelInfo = _zoomCursorView->getCelInfo(_zoomCursorLoop, _zoomCursorCel);
+ const byte *cursorBitmap = _zoomCursorView->getBitmap(_zoomCursorLoop, _zoomCursorCel);
+ _cursorSurface = new byte[cursorCelInfo->width * cursorCelInfo->height];
+ memcpy(_cursorSurface, cursorBitmap, cursorCelInfo->width * cursorCelInfo->height);
+
+ _zoomZone = zone;
+ kernelSetMoveZone(_zoomZone);
+
+ _zoomColor = zoomColor;
+ _zoomZoneActive = true;
+}
+
void GfxCursor::kernelSetPos(Common::Point pos) {
_coordAdjuster->setCursorPos(pos);
kernelMoveCursor(pos);
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 787841f5be..ae3b51e26a 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -40,6 +40,16 @@ class GfxPalette;
typedef Common::HashMap<int, GfxView *> CursorCache;
+struct SciCursorSetPositionWorkarounds {
+ SciGameId gameId;
+ int16 newPositionY;
+ int16 newPositionX;
+ int16 rectTop;
+ int16 rectLeft;
+ int16 rectBottom;
+ int16 rectRight;
+};
+
class GfxCursor {
public:
GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen);
@@ -69,6 +79,9 @@ public:
*/
void kernelSetMoveZone(Common::Rect zone);
+ void kernelClearZoomZone();
+ void kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor);
+
void kernelSetPos(Common::Point pos);
void kernelMoveCursor(Common::Point pos);
@@ -86,6 +99,16 @@ private:
bool _moveZoneActive;
Common::Rect _moveZone; // Rectangle in which the pointer can move
+ bool _zoomZoneActive;
+ Common::Rect _zoomZone;
+ GfxView *_zoomCursorView;
+ byte _zoomCursorLoop;
+ byte _zoomCursorCel;
+ GfxView *_zoomPicView;
+ byte _zoomColor;
+ byte _zoomMultiplier;
+ byte *_cursorSurface;
+
CursorCache _cachedCursors;
bool _isVisible;
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index a433b26ef2..fc374ea143 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -70,6 +70,7 @@ void GfxFrameout::kernelAddPlane(reg_t object) {
newPlane.pictureId = 0xFFFF;
newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
newPlane.lastPriority = 0xFFFF; // hidden
+ newPlane.planeOffsetX = 0;
_planes.push_back(newPlane);
kernelUpdatePlane(object);
@@ -91,6 +92,43 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
addPlanePicture(object, it->pictureId, 0);
}
}
+ it->planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
+ it->planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
+ it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom)) + 1;
+ it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right)) + 1;
+
+ Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
+ it->planeRect.top = (it->planeRect.top * screenRect.height()) / scriptsRunningHeight;
+ it->planeRect.left = (it->planeRect.left * screenRect.width()) / scriptsRunningWidth;
+ it->planeRect.bottom = (it->planeRect.bottom * screenRect.height()) / scriptsRunningHeight;
+ it->planeRect.right = (it->planeRect.right * screenRect.width()) / scriptsRunningWidth;
+
+ // We get negative left in kq7 in scrolling rooms
+ if (it->planeRect.left < 0) {
+ it->planeOffsetX = -it->planeRect.left;
+ it->planeRect.left = 0;
+ }
+ if (it->planeRect.top < 0)
+ it->planeRect.top = 0;
+ // We get bad plane-bottom in sq6
+ if (it->planeRect.right > _screen->getWidth())
+ it->planeRect.right = _screen->getWidth();
+ if (it->planeRect.bottom > _screen->getHeight())
+ it->planeRect.bottom = _screen->getHeight();
+
+ it->planeClipRect = Common::Rect(it->planeRect.width(), it->planeRect.height());
+ it->upscaledPlaneRect = it->planeRect;
+ it->upscaledPlaneClipRect = it->planeClipRect;
+ if (_screen->getUpscaledHires()) {
+ _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.top, it->upscaledPlaneRect.left);
+ _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.bottom, it->upscaledPlaneRect.right);
+ _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.top, it->upscaledPlaneClipRect.left);
+ _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.bottom, it->upscaledPlaneClipRect.right);
+ }
+
+ it->planePictureMirrored = readSelectorValue(_segMan, object, SELECTOR(mirrored));
+ it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back));
+
sortPlanes();
return;
}
@@ -98,6 +136,10 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
error("kUpdatePlane called on plane that wasn't added before");
}
+void GfxFrameout::kernelRepaintPlane(reg_t object) {
+ // TODO
+}
+
void GfxFrameout::kernelDeletePlane(reg_t object) {
deletePlanePictures(object);
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
@@ -147,6 +189,10 @@ void GfxFrameout::kernelAddScreenItem(reg_t object) {
_screenItems.push_back(object);
}
+void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
+ // TODO
+}
+
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
if (_screenItems[itemNr] == object) {
@@ -207,72 +253,31 @@ void GfxFrameout::kernelFrameout() {
_palette->palVaryUpdate();
// Allocate enough space for all screen items
+ // TODO: Modify _screenItems to hold FrameoutEntry entries instead.
+ // Creating and destroying this in kernelFrameout() is overkill!
FrameoutEntry *itemData = new FrameoutEntry[_screenItems.size()];
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
reg_t planeObject = it->object;
uint16 planeLastPriority = it->lastPriority;
- 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;
-
// Update priority here, sq6 sets it w/o UpdatePlane
uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
- 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;
-
- 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);
+ _paint32->fillRect(it->planeRect, 0);
continue;
}
- Common::Rect planeClipRect(planeRect.width(), planeRect.height());
-
- 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);
+ if (it->planeBack)
+ _paint32->fillRect(it->planeRect, it->planeBack);
GuiResourceId planeMainPictureId = it->pictureId;
- bool planePictureMirrored = false;
- if (readSelectorValue(_segMan, planeObject, SELECTOR(mirrored)))
- planePictureMirrored = true;
-
- _coordAdjuster->pictureSetDisplayArea(planeRect);
+ _coordAdjuster->pictureSetDisplayArea(it->planeRect);
_palette->drewPicture(planeMainPictureId);
// Fill our itemlist for this plane
@@ -360,25 +365,25 @@ void GfxFrameout::kernelFrameout() {
// 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();
+ int16 planeStartX = it->planeOffsetX;
+ int16 planeEndX = planeStartX + it->planeRect.width();
if (pictureCelEndX < planeStartX)
continue;
if (pictureCelStartX > planeEndX)
continue;
- int16 pictureOffsetX = planeOffsetX;
+ int16 pictureOffsetX = it->planeOffsetX;
int16 pictureX = itemEntry->x;
- if ((planeOffsetX) || (itemEntry->picStartX)) {
- if (planeOffsetX <= itemEntry->picStartX) {
- pictureX += itemEntry->picStartX - planeOffsetX;
+ if ((it->planeOffsetX) || (itemEntry->picStartX)) {
+ if (it->planeOffsetX <= itemEntry->picStartX) {
+ pictureX += itemEntry->picStartX - it->planeOffsetX;
pictureOffsetX = 0;
} else {
- pictureOffsetX = planeOffsetX - itemEntry->picStartX;
+ pictureOffsetX = it->planeOffsetX - itemEntry->picStartX;
}
}
- itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, planePictureMirrored);
+ itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored);
// warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority);
} else if (itemEntry->viewId != 0xFFFF) {
@@ -403,7 +408,7 @@ void GfxFrameout::kernelFrameout() {
break;
}
// Adjust according to current scroll position
- itemEntry->x -= planeOffsetX;
+ itemEntry->x -= it->planeOffsetX;
uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect));
if (useInsetRect) {
@@ -426,7 +431,7 @@ void GfxFrameout::kernelFrameout() {
Common::Rect nsRect = itemEntry->celRect;
// Translate back to actual coordinate within scrollable plane
- nsRect.translate(planeOffsetX, 0);
+ nsRect.translate(it->planeOffsetX, 0);
switch (getSciVersion()) {
case SCI_VERSION_2:
if (view->isSci2Hires()) {
@@ -465,13 +470,13 @@ void GfxFrameout::kernelFrameout() {
Common::Rect clipRect, translatedClipRect;
clipRect = itemEntry->celRect;
if (view->isSci2Hires()) {
- clipRect.clip(upscaledPlaneClipRect);
+ clipRect.clip(it->upscaledPlaneClipRect);
translatedClipRect = clipRect;
- translatedClipRect.translate(upscaledPlaneRect.left, upscaledPlaneRect.top);
+ translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top);
} else {
- clipRect.clip(planeClipRect);
+ clipRect.clip(it->planeClipRect);
translatedClipRect = clipRect;
- translatedClipRect.translate(planeRect.left, planeRect.top);
+ translatedClipRect.translate(it->planeRect.left, it->planeRect.top);
}
if (!clipRect.isEmpty()) {
@@ -501,8 +506,8 @@ void GfxFrameout::kernelFrameout() {
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;
+ uint16 curX = itemEntry->x + it->planeRect.left;
+ uint16 curY = itemEntry->y + it->planeRect.top;
for (uint32 i = 0; i < text.size(); i++) {
unsigned char curChar = text[i];
// TODO: proper text splitting... this is a hack
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index f8f7e54a27..07297a91af 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -32,7 +32,14 @@ struct PlaneEntry {
reg_t object;
uint16 priority;
uint16 lastPriority;
+ int16 planeOffsetX;
GuiResourceId pictureId;
+ Common::Rect planeRect;
+ Common::Rect planeClipRect;
+ Common::Rect upscaledPlaneRect;
+ Common::Rect upscaledPlaneClipRect;
+ bool planePictureMirrored;
+ byte planeBack;
};
typedef Common::List<PlaneEntry> PlaneList;
@@ -81,8 +88,10 @@ public:
void kernelAddPlane(reg_t object);
void kernelUpdatePlane(reg_t object);
+ void kernelRepaintPlane(reg_t object);
void kernelDeletePlane(reg_t object);
void kernelAddScreenItem(reg_t object);
+ void kernelUpdateScreenItem(reg_t object);
void kernelDeleteScreenItem(reg_t object);
int16 kernelGetHighPlanePri();
void kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId);
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 630626c128..06470bc560 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -905,7 +905,7 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack
_paint16->fillRect(_ports->_menuBarRect, 1, colorBack);
_ports->penColor(colorPen);
_ports->moveTo(0, 1);
- _text16->Draw_String(text);
+ _text16->Draw_Status(text);
_paint16->bitsShow(_ports->_menuBarRect);
_ports->setPort(oldPort);
}
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index dbc738e2f7..3c115f0c8e 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -380,6 +380,14 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo);
_transitions->setup(animationNr, animationBlackoutFlag);
} else {
+ // We need to set it for SCI1EARLY+ (sierra sci also did so), otherwise we get at least the following issues:
+ // LSL5 (english) - last wakeup (taj mahal flute dream)
+ // SQ5 (english v1.03) - during the scene following the scrubbing
+ // in both situations a window is shown when kDrawPic is called, which would result otherwise in
+ // no showpic getting called from kAnimate and we would get graphic corruption
+ // XMAS1990 EGA did not set it in this case, VGA did
+ if (getSciVersion() >= SCI_VERSION_1_EARLY)
+ _screen->_picNotValid = 1;
_ports->beginUpdate(_ports->_picWind);
drawPicture(pictureId, animationNr, mirroredFlag, addToFlag, EGApaletteNo);
_ports->endUpdate(_ports->_picWind);
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 5c17f76558..76b2ed53fc 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -340,7 +340,8 @@ void GfxPalette::drewPicture(GuiResourceId pictureId) {
_sysPalette.timestamp++;
if (_palVaryResourceId != -1) {
- palVaryLoadTargetPalette(pictureId);
+ if (g_sci->getEngineState()->gameIsRestarting == 0) // only if not restored nor restarted
+ palVaryLoadTargetPalette(pictureId);
}
}
@@ -613,9 +614,18 @@ bool GfxPalette::kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint1
_palVaryStepStop = stepStop;
_palVaryDirection = direction;
// if no ticks are given, jump directly to destination
- if (!_palVaryTicks)
+ if (!_palVaryTicks) {
_palVaryDirection = stepStop;
- palVaryInstallTimer();
+ // sierra sci set the timer to 1 tick instead of calling it directly
+ // we have to change this to prevent a race condition to happen in
+ // at least freddy pharkas during nighttime. In that case kPalVary is
+ // called right before a transition and because we load pictures much
+ // faster, the 1 tick won't pass sometimes resulting in the palette
+ // being daytime instead of nighttime during the transition.
+ palVaryProcess(1, true);
+ } else {
+ palVaryInstallTimer();
+ }
return true;
}
return false;
@@ -632,9 +642,14 @@ int16 GfxPalette::kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direc
_palVaryStepStop = stepStop;
_palVaryDirection = direction != -1 ? -direction : -_palVaryDirection;
- if (!_palVaryTicks)
+ if (!_palVaryTicks) {
_palVaryDirection = _palVaryStepStop - _palVaryStep;
- palVaryInstallTimer();
+ // ffs. see palVaryInit right above, we fix the code here as well
+ // just in case
+ palVaryProcess(1, true);
+ } else {
+ palVaryInstallTimer();
+ }
return kernelPalVaryGetCurrentStep();
}
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index e568316919..39666b82cb 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -120,10 +120,6 @@ void GfxPicture::drawSci11Vga() {
// [priorityBandData:WORD] * priorityBandCount
// [priority:BYTE] [unknown:BYTE]
- // Create palette and set it
- _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
- _palette->set(&palette, true);
-
// priority bands are supposed to be 14 for sci1.1 pictures
assert(priorityBandsCount == 14);
@@ -132,8 +128,13 @@ void GfxPicture::drawSci11Vga() {
}
// display Cel-data
- if (has_cel)
+ if (has_cel) {
+ // Create palette and set it
+ _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
+ _palette->set(&palette, true);
+
drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0);
+ }
// process vector data
drawVectorData(inbuffer + vector_dataPos, vector_size);
@@ -852,11 +853,11 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
// Now remove screens, that already got the right color/priority/control
if ((screenMask & GFX_SCREEN_MASK_VISUAL) && (searchColor == color))
- screenMask ^= GFX_SCREEN_MASK_VISUAL;
+ screenMask &= ~GFX_SCREEN_MASK_VISUAL;
if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && (searchPriority == priority))
- screenMask ^= GFX_SCREEN_MASK_PRIORITY;
+ screenMask &= ~GFX_SCREEN_MASK_PRIORITY;
if ((screenMask & GFX_SCREEN_MASK_CONTROL) && (searchControl == control))
- screenMask ^= GFX_SCREEN_MASK_CONTROL;
+ screenMask &= ~GFX_SCREEN_MASK_CONTROL;
// Exit, if no screens left
if (!screenMask)
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 8f4fe094a8..f21fa39476 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -185,7 +185,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
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())
+ g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame)
userAbort = true;
curPosition = _audio->getAudioPosition();
} while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort));
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index dddd9b1c86..e7f319a25c 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -105,16 +105,9 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te
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;
- }
+ case GID_MOTHERGOOSE256:
+ // only the SCI1 and SCI1.1 (VGA) versions need this
+ offTop = 0;
break;
case GID_FAIRYTALES:
// Mixed-Up Fairy Tales (& its demo) uses -w 26 0 200 320. If we don't
@@ -308,7 +301,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
Common::Rect r;
if (!pwnd) {
- error("Can't open window!");
+ error("Can't open window");
return 0;
}
@@ -376,6 +369,20 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
if (draw)
drawWindow(pwnd);
setPort((Port *)pwnd);
+
+ // FIXME: changing setOrigin to not clear the rightmost bit fixes the display of windows
+ // in some fanmade games (e.g. New Year's Mystery (Updated)). Since the fanmade games
+ // use an unmodified SCI interpreter, this leads me to believe that there either is some
+ // off-by-one error in the window drawing code, or there is another place where the
+ // rightmost bit should be cleeared. New Year's Mystery is a good test case for this, as
+ // it draws dialogs and then draws cels on top of them, for fancier dialog corners (like,
+ // for example, KQ5). KQ5 has a custom window style, however, whereas New Year's mystery
+ // has a "classic" style with only SCI_WINDOWMGR_STYLE_NOFRAME set. If
+ // SCI_WINDOWMGR_STYLE_NOFRAME is removed, the window is cleared correctly, because it
+ // grows slightly, covering the view pixels on the left. In any case, the views and the
+ // window have a difference of one pixel when they're drawn via kNewWindow and kDrawCel,
+ // which causes the glitch to appear when the window is closed.
+
// All SCI0 games till kq4 .502 (not including) did not adjust against _wmgrPort, we set _wmgrPort->top to 0 in that case
setOrigin(pwnd->rect.left, pwnd->rect.top + _wmgrPort->top);
pwnd->rect.moveTo(0, 0);
diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp
index 1572a0a9ec..0792c6596e 100644
--- a/engines/sci/graphics/robot.cpp
+++ b/engines/sci/graphics/robot.cpp
@@ -37,7 +37,6 @@ GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId res
: _resMan(resMan), _screen(screen), _resourceId(resourceId) {
assert(resourceId != -1);
initData(resourceId);
- _resourceData = 0;
}
GfxRobot::~GfxRobot() {
@@ -57,119 +56,13 @@ void GfxRobot::initData(GuiResourceId resourceId) {
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;
- }
-
-// sample data:
-// Header - 14 bytes
-// DWORD:Sample Size - 2 needs to be subtracted (??!!)
-// ???
-// Actual samples following
-
-// version may be 3, 4 and 5
-// version 3 has a different header (unknown to this point)
-//
-// main header (56 bytes + 2 bytes resource id)
-// followed by sample data if hasSound == 1
-//
-
-// 90.rbt (640x390, 22050, 1 16, ADPCM) 67 frames
-// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 f0 00 43 00 ..SOL.........C.
-// ^ signature ^ version ^ ^ ^ frames
-// ^ 2221
-// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00
-// ^ ^ ^ ^ ^ ^ ^ ^ ^
-// hasSound
-// 00000020: 03 00 01 00 00 cf 03 00-00 00 00 00 00 00 00 00
-// ^ ^ ^ pixel count ^
-// ^
-// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00
-// ^ ^
-// Sample-Data (Header):
-// compression must be 0 for now
-// 0000003c: f2 9f 00 00 00 00 d2 4d 00 00 20 52-00 00
-// ^ ^ ^
-// byte count compression
-// 40946
-// Actual Samples following
-// a5 11 04 02 85 90 ...M.. R........
-//
-// Offset 41020
-// Palette
-
-// 91.rbt (320x240, 22050, 1 16, ADPCM) 90 frames
-// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 f0 00 5a 00 ..SOL.........Z.
-// ^ frames
-// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00 ................
-// 00000020: 03 00 01 00 00 2c 01 00-00 00 00 00 00 00 00 00 .....,..........
-// ^ pixel count
-// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 f2 9f 00 00 ................ offset 60
-// ^ data begin (sample)
-// 00000040: 00 00 d2 4d 00 00 20 52-00 00 82 01 00 01 00 01 ...M.. R........
-// ...
-// 0000a030: 8d 8d 8f 8e 8f 90 90 91-92 92 92 94 0e 00 00 00 ................ offset 41004
-// ^ palette start
-// 0000a040: 00 00 00 00 00 00 01 00-00 09 01 00 00 00 00 00 ................
-// 0000a050: 00 00 00 00 00 37 00 00-00 51 00 01 01 00 00 00 .....7...Q......
-// ^ color start^ color count
-// 0000a060: 00 58 6b 2b 4b 69 28 50-5b 24 68 50 20 5b 53 21 .Xk+Ki(P[$hP [S!
-// ^ start pal data
-// [...]
-// 0000a110: 24 05 41 14 04 18 25 10-64 00 00 2d 18 05 58 00 $.A...%.d..-..X.
-// 0000a120: 00 16 20 07 50 00 00 20-19 01 2d 0e 00 48 00 00 .. .P.. ..-..H..
-// 0000a130: 40 00 00 10 18 05 38 00-00 30 00 00 28 00 00 0b @.....8..0..(...
-// 0000a140: 0e 00 20 00 00 18 00 00-00 08 00 10 00 00 08 00 .. .............
-// 0000a150: 00 00 00 00 70 70 70 70-70 70 70 70 70 70 70 70 ....pppppppppppp
-// [...]
-// 0000a4e0: 70 70 70 70 70 70 70 70-70 70 70 70 34 0a 75 0a pppppppppppp4.u.
-// 0000a4f0: 4a 0b c5 0b f4 0b 54 0c-bd 0c 7a 0d 91 0e 1f 10 J.....T...z.....
-// 0000a500: 16 12 72 14 19 17 ef 19-9a 1c b3 1e 79 20 c1 22 ..r.........y ."
-// 0000a510: 33 22 33 23 e0 25 84 26-eb 26 1a 2d 43 2d af 2d 3"3#.%.&.&.-C-.-
-// [...]
-// 0000aff0: 20 20 20 20 20 20 20 20-20 20 20 20 20 20 20 20
-// 0000b000: 01 00 7f 64 40 01 f0 00-00 00 00 00 00 00 00 00 ...d@...........
-// ^width^height
-// 0000b010: 1c 0a 02 00 7f 7f 7f 7f-04 08 00 00 00 f0 00 00 ................
-// 0000b020: 00 00 43 e0 7f ff ff ff-ff ff ff ff ff ff ff ff ..C.............
-// 0000b030: ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
-
-// 161.rbt (112x155, 22050, 1 16, ADPCM) 29 frames
-// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 96 00 1d 00 ..SOL...........
-// ^ frames
-// 00000010: b0 04 00 a0 00 00 00 00-01 01 00 00 0a 00 01 00 ................
-// 00000020: 03 00 01 00 47 3e 00 00-00 00 00 00 00 00 00 00 ....G>..........
-// ^ pixel count
-// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 f2 9f 00 00 ................
-// ^ data begin (sample)
-// 00000040: 00 00 d2 4d 00 00 20 52-00 00 00 00 00 00 00 00 ...M.. R........
-// 00000050: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-
-// 213.rbt (125x248, nosound) 30 frames
-// 00000000: 16 00 53 4f 4c 00 05 00-ad 08 00 00 96 00 1e 00 ..SOL...........
-// ^ frames
-// 00000010: b0 04 00 00 00 00 00 00-01 00 00 00 0a 00 01 00 ................
-// ^ ?! ^ no sound?!
-// 00000020: 03 00 01 00 82 6e 00 00-00 00 00 00 00 00 00 00 .....n..........
-// ^ pixel count
-// 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 0e 00 00 00 ................
-// ^ data begin (palette)
-// 00000040: 00 00 00 00 00 00 01 00-00 ca 00 00 00 00 00 00 ................
-// 00000050: 00 00 00 00 00 37 00 00-00 3c 00 01 01 00 00 00 .....7...<......
-// 00000060: 00 d0 d0 c0 d0 c0 a8 c8-b8 c0 d0 b0 a0 c0 a8 88
-// ^ palette data start
-// 00000070: c0 a0 a0 c8 98 90 d0 88-60 b0 90 80 b8 88 80 a0 ........`.......
-// 00000080: 90 98 b0 88 90 c0 78 60-a0 80 80 a0 80 70 c8 70 ......x`.....p.p
-// [...]
-// 00000110: 00 00 00 00 08 70 70 70-70 70 70 70 70 70 70 70 .....ppppppppppp
-// ^ ??
-// 00000120: 70 70 70 70 70 70 70 70-70 70 70 70 70 70 70 70 pppppppppppppppp
+ // The RBT video starts with a SOL audio file, followed by
+ // video data which is appended after it
_frameCount = READ_LE_UINT16(_resourceData + 14);
+ _audioSize = READ_LE_UINT16(_resourceData + 15);
+
//_frameSize = READ_LE_UINT32(_resourceData + 34);
byte hasSound = _resourceData[25];
@@ -179,21 +72,57 @@ void GfxRobot::initData(GuiResourceId resourceId) {
// TODO: just trying around in here...
void GfxRobot::draw() {
- byte *bitmapData = _resourceData + ROBOT_FILE_STARTOFDATA;
+ byte *bitmapData = _resourceData + _audioSize;
int x, y;
- //int frame;
+ int frame;
return;
- //for (frame = 0; frame < 30; frame++) {
- for (y = 0; y < _height; y++) {
- for (x = 0; x < _width; x++) {
- _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, *bitmapData, 0, 0);
- bitmapData++;
+ // Each frame contains these bytes:
+ // 01 00 7f 64 - always the same, perhaps resource type + extra
+ // 40 01 - total frame width (320 in this case)
+ // f0 00 - total frame height (240 in this case)
+ // The total video size is calculated from the maximum width, height
+ // of all the frames in the robot file
+ // 4 zeroes
+ // 4 bytes, perhaps frame x, y on screen?
+ // 2 bytes, unknown
+ // 2 bytes, a small number (e.g. 01 00 or 02 00)
+ // 7f 7f - 127x127
+ // 7f 7f - 127x127
+ // 2 bytes, related to frame size?
+ // 00 00
+ // 00 f0
+ // 4 zeroes
+ // 43 e0
+ // 7f ff
+
+ // The frames themselves seem to contain a size of the actual drawn data
+ // on screen. The frame data seems to be uncompressed, placed on screen
+ // at appropriate x,y coordinates, and each frame can have a different size.
+ // This is apparent from the fact that a 320x240 frame (e.g. in Phantasmagoria
+ // demo, 91.rbt) has 4833, 4898, 5111, etc bytes, whereas a full frame would
+ // be 320x240 = 76800 bytes. Thus, each frame is either somehow compressed
+ // (but the data seems uncompressed?), or only the part that changes is drawn
+ // on screen, something like the MPEG I-frames
+
+ for (frame = 0; frame < _frameCount; frame++) {
+ bitmapData += 4; // skip header bytes
+ _width = READ_LE_UINT16(bitmapData + 4); bitmapData += 2;
+ _height = READ_LE_UINT16(bitmapData + 6); bitmapData += 2;
+
+ for (y = 0; y < _width; y++) {
+ for (x = 0; x < _height; x++) {
+ _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, *bitmapData, 0, 0);
+ bitmapData++;
+ }
}
+
+ _screen->copyToScreen();
+ // Sleep for a second
+ g_sci->sleep(1000);
}
- //}
- _screen->copyToScreen();
+
}
#endif
diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h
index 3ea9a7f735..76dca35a82 100644
--- a/engines/sci/graphics/robot.h
+++ b/engines/sci/graphics/robot.h
@@ -51,6 +51,7 @@ private:
uint16 _height;
uint16 _frameCount;
uint32 _frameSize; // is width * height (pixelCount)
+ uint16 _audioSize;
};
#endif
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 839b9975c5..6eabc7c9f0 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -345,11 +345,11 @@ byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byt
int offset = y * _width + x;
byte match = 0;
- if (screenMask & GFX_SCREEN_MASK_VISUAL && *(_visualScreen + offset) == t_color)
+ if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color)
match |= GFX_SCREEN_MASK_VISUAL;
- if (screenMask & GFX_SCREEN_MASK_PRIORITY && *(_priorityScreen + offset) == t_pri)
+ if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri)
match |= GFX_SCREEN_MASK_PRIORITY;
- if (screenMask & GFX_SCREEN_MASK_CONTROL && *(_controlScreen + offset) == t_con)
+ if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con)
match |= GFX_SCREEN_MASK_CONTROL;
return match;
}
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index f5eb268863..3fba3006c7 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -30,6 +30,7 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
#include "sci/graphics/cache.h"
+#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/font.h"
@@ -88,7 +89,7 @@ void GfxText16::ClearChar(int16 chr) {
// 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) {
+int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor, bool doingDrawing) {
const char *textCode = text;
int16 textCodeSize = 0;
char curCode;
@@ -126,8 +127,20 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1
}
}
break;
- case 'r': // reference?!
- // Used in Pepper, no idea how this works out
+ case 'r': // reference (used in pepper)
+ if (doingDrawing) {
+ if (_codeRefTempRect.top == -1) {
+ // Starting point
+ _codeRefTempRect.top = _ports->_curPort->curTop;
+ _codeRefTempRect.left = _ports->_curPort->curLeft;
+ } else {
+ // End point reached
+ _codeRefTempRect.bottom = _ports->_curPort->curTop + _ports->_curPort->fontHeight;
+ _codeRefTempRect.right = _ports->_curPort->curLeft;
+ _codeRefRects.push_back(_codeRefTempRect);
+ _codeRefTempRect.left = _codeRefTempRect.top = -1;
+ }
+ }
break;
}
return textCodeSize;
@@ -162,7 +175,7 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
case 0x7C:
if (getSciVersion() >= SCI_VERSION_1_1) {
curCharCount++;
- curCharCount += CodeProcessing(text, orgFontId, previousPenColor);
+ curCharCount += CodeProcessing(text, orgFontId, previousPenColor, false);
continue;
}
break;
@@ -258,7 +271,7 @@ void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId org
break;
case 0x7C:
if (getSciVersion() >= SCI_VERSION_1_1) {
- len -= CodeProcessing(text, orgFontId, 0);
+ len -= CodeProcessing(text, orgFontId, 0, false);
break;
}
default:
@@ -359,7 +372,7 @@ void GfxText16::Draw(const char *text, int16 from, int16 len, GuiResourceId orgF
break;
case 0x7C:
if (getSciVersion() >= SCI_VERSION_1_1) {
- len -= CodeProcessing(text, orgFontId, orgPenColor);
+ len -= CodeProcessing(text, orgFontId, orgPenColor, true);
break;
}
default:
@@ -408,6 +421,10 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
doubleByteMode = true;
}
+ // Reset reference code rects
+ _codeRefRects.clear();
+ _codeRefTempRect.left = _codeRefTempRect.top = -1;
+
maxTextWidth = 0;
while (*text) {
charCount = GetLongest(text, rect.width(), fontId);
@@ -474,6 +491,32 @@ void GfxText16::Draw_String(const char *text) {
_ports->penColor(previousPenColor);
}
+// we need to have a separate status drawing code
+// In KQ4 the IV char is actually 0xA, which would otherwise get considered as linebreak and not printed
+void GfxText16::Draw_Status(const char *text) {
+ uint16 curChar, charWidth;
+ uint16 textLen = strlen(text);
+ Common::Rect rect;
+
+ GetFont();
+ if (!_font)
+ return;
+
+ rect.top = _ports->_curPort->curTop;
+ rect.bottom = rect.top + _ports->_curPort->fontHeight;
+ while (textLen--) {
+ curChar = (*(const byte *)text++);
+ switch (curChar) {
+ case 0:
+ break;
+ default:
+ charWidth = _font->getCharWidth(curChar);
+ _font->draw(curChar, _ports->_curPort->top + _ports->_curPort->curTop, _ports->_curPort->left + _ports->_curPort->curLeft, _ports->_curPort->penClr, _ports->_curPort->greyedOutput);
+ _ports->_curPort->curLeft += charWidth;
+ }
+ }
+}
+
// 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) {
@@ -485,6 +528,30 @@ bool GfxText16::SwitchToFont900OnSjis(const char *text) {
return false;
}
+reg_t GfxText16::allocAndFillReferenceRectArray() {
+ uint rectCount = _codeRefRects.size();
+ if (rectCount) {
+ reg_t rectArray;
+ byte *rectArrayPtr = g_sci->getEngineState()->_segMan->allocDynmem(4 * 2 * (rectCount + 1), "text code reference rects", &rectArray);
+ GfxCoordAdjuster *coordAdjuster = g_sci->_gfxCoordAdjuster;
+ for (uint curRect = 0; curRect < rectCount; curRect++) {
+ coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].left, _codeRefRects[curRect].top);
+ coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].right, _codeRefRects[curRect].bottom);
+ WRITE_LE_UINT16(rectArrayPtr + 0, _codeRefRects[curRect].left);
+ WRITE_LE_UINT16(rectArrayPtr + 2, _codeRefRects[curRect].top);
+ WRITE_LE_UINT16(rectArrayPtr + 4, _codeRefRects[curRect].right);
+ WRITE_LE_UINT16(rectArrayPtr + 6, _codeRefRects[curRect].bottom);
+ rectArrayPtr += 8;
+ }
+ WRITE_LE_UINT16(rectArrayPtr + 0, 0x7777);
+ WRITE_LE_UINT16(rectArrayPtr + 2, 0x7777);
+ WRITE_LE_UINT16(rectArrayPtr + 4, 0x7777);
+ WRITE_LE_UINT16(rectArrayPtr + 6, 0x7777);
+ return rectArray;
+ }
+ return NULL_REG;
+}
+
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);
diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h
index 9b8b6d9f19..dc3ed2f62b 100644
--- a/engines/sci/graphics/text16.h
+++ b/engines/sci/graphics/text16.h
@@ -32,6 +32,8 @@ namespace Sci {
#define SCI_TEXT16_ALIGNMENT_CENTER 1
#define SCI_TEXT16_ALIGNMENT_LEFT 0
+typedef Common::Array<Common::Rect> CodeRefRectArray;
+
class GfxPorts;
class GfxPaint16;
class GfxScreen;
@@ -48,7 +50,7 @@ public:
GfxFont *GetFont();
void SetFont(GuiResourceId fontId);
- int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor);
+ int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor, bool doingDrawing);
void ClearChar(int16 chr);
@@ -62,9 +64,12 @@ public:
void Show(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor);
void Box(const char *text, int16 bshow, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId);
void Draw_String(const char *text);
+ void Draw_Status(const char *text);
GfxFont *_font;
+ reg_t allocAndFillReferenceRectArray();
+
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);
@@ -83,6 +88,9 @@ private:
GuiResourceId *_codeFonts;
int _codeColorsCount;
uint16 *_codeColors;
+
+ Common::Rect _codeRefTempRect;
+ CodeRefRectArray _codeRefRects;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index abb5e74cbd..3f4ce7bbc8 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -37,6 +37,8 @@
namespace Sci {
+//#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly)
+
GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA)
: _screen(screen), _palette(palette), _isVGA(isVGA) {
init();
@@ -116,16 +118,33 @@ void GfxTransitions::init() {
void GfxTransitions::setup(int16 number, bool blackoutFlag) {
if (number != -1) {
+#ifndef DISABLE_TRANSITIONS
_number = number;
+#else
+ _number = SCI_TRANSITIONS_NONE;
+#endif
_blackoutFlag = blackoutFlag;
}
}
-void GfxTransitions::updateScreenAndWait(int msec) {
+bool GfxTransitions::doCreateFrame(uint32 shouldBeAtMsec) {
+ uint32 msecPos = g_system->getMillis() - _transitionStartTime;
+
+ if (shouldBeAtMsec > msecPos)
+ return true;
+ return false;
+}
+
+void GfxTransitions::updateScreenAndWait(uint32 shouldBeAtMsec) {
Common::Event ev;
- g_system->updateScreen();
- g_system->delayMillis(msec);
+
while (g_system->getEventManager()->pollEvent(ev)) {} // discard all events
+
+ g_system->updateScreen();
+ // if we have still some time left, delay accordingly
+ uint32 msecPos = g_system->getMillis() - _transitionStartTime;
+ if (shouldBeAtMsec > msecPos)
+ g_system->delayMillis(shouldBeAtMsec - msecPos);
}
// will translate a number and return corresponding translationEntry
@@ -191,6 +210,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
setNewPalette(blackoutFlag);
}
+ _transitionStartTime = g_system->getMillis();
switch (number) {
case SCI_TRANSITIONS_VERTICALROLL_FROMCENTER:
verticalRollFromCenter(blackoutFlag);
@@ -285,11 +305,14 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag
void GfxTransitions::fadeOut() {
byte oldPalette[4 * 256], workPalette[4 * 256];
int16 stepNr, colorNr;
+ // Sierra did not fade in/out color 255 for sci1.1, but they used it in
+ // several pictures (e.g. qfg3 demo/intro), so the fading looked weird
+ int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 256 : 255;
g_system->grabPalette(oldPalette, 0, 256);
for (stepNr = 100; stepNr >= 0; stepNr -= 10) {
- for (colorNr = 1; colorNr < 255; colorNr++){
+ for (colorNr = 1; colorNr < tillColorNr; colorNr++){
workPalette[colorNr * 4 + 0] = oldPalette[colorNr * 4] * stepNr / 100;
workPalette[colorNr * 4 + 1] = oldPalette[colorNr * 4 + 1] * stepNr / 100;
workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100;
@@ -303,9 +326,12 @@ void GfxTransitions::fadeOut() {
// the load
void GfxTransitions::fadeIn() {
int16 stepNr;
+ // Sierra did not fade in/out color 255 for sci1.1, but they used it in
+ // several pictures (e.g. qfg3 demo/intro), so the fading looked weird
+ int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 256 : 255;
for (stepNr = 0; stepNr <= 100; stepNr += 10) {
- _palette->kernelSetIntensity(1, 255, stepNr, true);
+ _palette->kernelSetIntensity(1, tillColorNr, stepNr, true);
g_sci->getEngineState()->wait(2);
}
}
@@ -315,6 +341,7 @@ void GfxTransitions::fadeIn() {
void GfxTransitions::pixelation(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
Common::Rect pixelRect;
+ uint32 msecCount = 0;
do {
mask = (mask & 1) ? (mask >> 1) ^ 0xB400 : mask >> 1;
@@ -326,7 +353,8 @@ void GfxTransitions::pixelation(bool blackoutFlag) {
if (!pixelRect.isEmpty())
copyRectToScreen(pixelRect, blackoutFlag);
if ((stepNr & 0x3FF) == 0) {
- updateScreenAndWait(5);
+ msecCount += 9;
+ updateScreenAndWait(msecCount);
}
stepNr++;
} while (mask != 0x40);
@@ -337,6 +365,7 @@ void GfxTransitions::pixelation(bool blackoutFlag) {
void GfxTransitions::blocks(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
Common::Rect blockRect;
+ uint32 msecCount = 0;
do {
mask = (mask & 1) ? (mask >> 1) ^ 0x240 : mask >> 1;
@@ -348,7 +377,8 @@ void GfxTransitions::blocks(bool blackoutFlag) {
if (!blockRect.isEmpty())
copyRectToScreen(blockRect, blackoutFlag);
if ((stepNr & 7) == 0) {
- updateScreenAndWait(4);
+ msecCount += 5;
+ updateScreenAndWait(msecCount);
}
stepNr++;
} while (mask != 0x40);
@@ -359,6 +389,7 @@ void GfxTransitions::blocks(bool blackoutFlag) {
void GfxTransitions::straight(int16 number, bool blackoutFlag) {
int16 stepNr = 0;
Common::Rect newScreenRect = _picRect;
+ uint32 msecCount = 0;
switch (number) {
case SCI_TRANSITIONS_STRAIGHT_FROM_RIGHT:
@@ -366,7 +397,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
while (newScreenRect.left >= _picRect.left) {
copyRectToScreen(newScreenRect, blackoutFlag);
if ((stepNr & 1) == 0) {
- updateScreenAndWait(1);
+ msecCount += 2;
+ updateScreenAndWait(msecCount);
}
stepNr++;
newScreenRect.translate(-1, 0);
@@ -378,7 +410,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
while (newScreenRect.right <= _picRect.right) {
copyRectToScreen(newScreenRect, blackoutFlag);
if ((stepNr & 1) == 0) {
- updateScreenAndWait(1);
+ msecCount += 2;
+ updateScreenAndWait(msecCount);
}
stepNr++;
newScreenRect.translate(1, 0);
@@ -389,7 +422,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
newScreenRect.top = newScreenRect.bottom - 1;
while (newScreenRect.top >= _picRect.top) {
copyRectToScreen(newScreenRect, blackoutFlag);
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
stepNr++;
newScreenRect.translate(0, -1);
}
@@ -399,7 +433,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
newScreenRect.bottom = newScreenRect.top + 1;
while (newScreenRect.bottom <= _picRect.bottom) {
copyRectToScreen(newScreenRect, blackoutFlag);
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
stepNr++;
newScreenRect.translate(0, 1);
}
@@ -428,6 +463,7 @@ void GfxTransitions::scroll(int16 number) {
Common::Rect oldScreenRect = _picRect;
Common::Rect newMoveRect = _picRect;
Common::Rect newScreenRect = _picRect;
+ uint32 msecCount = 0;
_screen->copyFromScreen(_oldScreen);
screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight();
@@ -438,42 +474,36 @@ void GfxTransitions::scroll(int16 number) {
newMoveRect.left = newMoveRect.right;
while (oldMoveRect.left < oldMoveRect.right) {
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) {
- updateScreenAndWait(1);
+ msecCount += 5;
+ if (doCreateFrame(msecCount)) {
+ if (oldMoveRect.right > oldMoveRect.left)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
+ _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
+ updateScreenAndWait(msecCount);
+ }
}
stepNr++;
}
- 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++; 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) {
- updateScreenAndWait(1);
+ msecCount += 5;
+ if (doCreateFrame(msecCount)) {
+ if (oldMoveRect.right > oldMoveRect.left)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
+ _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
+ updateScreenAndWait(msecCount);
+ }
}
stepNr++;
}
- 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:
@@ -481,11 +511,15 @@ void GfxTransitions::scroll(int16 number) {
newMoveRect.top = newMoveRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
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);
+
+ msecCount += 5;
+ if (doCreateFrame(msecCount)) {
+ if (oldMoveRect.top < oldMoveRect.bottom)
+ scrollCopyOldToScreen(oldScreenRect, _picRect.left, _picRect.top);
+ _screen->copyRectToScreen(newScreenRect, newMoveRect.left, newMoveRect.top);
+ updateScreenAndWait(msecCount);
+ }
}
break;
@@ -493,14 +527,22 @@ void GfxTransitions::scroll(int16 number) {
newScreenRect.top = newScreenRect.bottom;
while (oldMoveRect.top < oldMoveRect.bottom) {
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);
+
+ msecCount += 5;
+ if (doCreateFrame(msecCount)) {
+ if (oldMoveRect.top < oldMoveRect.bottom)
+ scrollCopyOldToScreen(oldScreenRect, oldMoveRect.left, oldMoveRect.top);
+ _screen->copyRectToScreen(newScreenRect, _picRect.left, _picRect.top);
+ updateScreenAndWait(msecCount);
+ }
}
break;
}
+
+ // Copy over final position just in case
+ _screen->copyRectToScreen(newScreenRect);
+ g_system->updateScreen();
}
// Vertically displays new screen starting from center - works on _picRect area
@@ -508,6 +550,7 @@ void GfxTransitions::scroll(int16 number) {
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);
+ uint32 msecCount = 0;
while ((leftRect.left >= _picRect.left) || (rightRect.right <= _picRect.right)) {
if (leftRect.left < _picRect.left)
@@ -516,7 +559,8 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
rightRect.translate(-1, 0);
copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(-1, 0);
copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(1, 0);
- updateScreenAndWait(2);
+ msecCount += 3;
+ updateScreenAndWait(msecCount);
}
}
@@ -525,11 +569,13 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
Common::Rect rightRect = Common::Rect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom);
+ uint32 msecCount = 0;
while (leftRect.left < rightRect.right) {
copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0);
copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(-1, 0);
- updateScreenAndWait(2);
+ msecCount += 3;
+ updateScreenAndWait(msecCount);
}
}
@@ -538,6 +584,7 @@ void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
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);
+ uint32 msecCount = 0;
while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) {
if (upperRect.top < _picRect.top)
@@ -546,7 +593,8 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
lowerRect.translate(0, -1);
copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, -1);
copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, 1);
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
}
}
@@ -555,11 +603,13 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
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);
+ uint32 msecCount = 0;
while (upperRect.top < lowerRect.bottom) {
copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, 1);
copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, -1);
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
}
}
@@ -571,6 +621,7 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
Common::Rect lowerRect(upperRect.left, upperRect.top, upperRect.right, upperRect.bottom);
Common::Rect leftRect(upperRect.left, upperRect.top, upperRect.left + 1, lowerRect.bottom);
Common::Rect rightRect(upperRect.right, upperRect.top, upperRect.right + 1, lowerRect.bottom);
+ uint32 msecCount = 0;
while ((upperRect.top >= _picRect.top) || (lowerRect.bottom <= _picRect.bottom)) {
if (upperRect.top < _picRect.top) {
@@ -589,7 +640,8 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, 1); lowerRect.left--; lowerRect.right++;
copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(-1, 0); leftRect.top--; leftRect.bottom++;
copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(1, 0); rightRect.top--; rightRect.bottom++;
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
}
}
@@ -600,13 +652,15 @@ void GfxTransitions::diagonalRollToCenter(bool blackoutFlag) {
Common::Rect lowerRect(_picRect.left, _picRect.bottom - 1, _picRect.right, _picRect.bottom);
Common::Rect leftRect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
Common::Rect rightRect(_picRect.right - 1, _picRect.top, _picRect.right, _picRect.bottom);
+ uint32 msecCount = 0;
while (upperRect.top < lowerRect.bottom) {
copyRectToScreen(upperRect, blackoutFlag); upperRect.translate(0, 1); upperRect.left++; upperRect.right--;
copyRectToScreen(lowerRect, blackoutFlag); lowerRect.translate(0, -1); lowerRect.left++; lowerRect.right--;
copyRectToScreen(leftRect, blackoutFlag); leftRect.translate(1, 0);
copyRectToScreen(rightRect, blackoutFlag); rightRect.translate(-1, 0);
- updateScreenAndWait(3);
+ msecCount += 4;
+ updateScreenAndWait(msecCount);
}
}
diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h
index 233638ffda..674b7a8173 100644
--- a/engines/sci/graphics/transitions.h
+++ b/engines/sci/graphics/transitions.h
@@ -91,7 +91,8 @@ private:
void horizontalRollToCenter(bool blackoutFlag);
void diagonalRollFromCenter(bool blackoutFlag);
void diagonalRollToCenter(bool blackoutFlag);
- void updateScreenAndWait(int msec);
+ bool doCreateFrame(uint32 shouldBeAtMsec);
+ void updateScreenAndWait(uint32 shouldBeAtMsec);
GfxScreen *_screen;
GfxPalette *_palette;
@@ -102,6 +103,8 @@ private:
bool _blackoutFlag;
Common::Rect _picRect;
byte *_oldScreen; // buffer for saving current active screen data to, has dimenions of _screen->_displayScreen
+
+ uint32 _transitionStartTime; // when the current transition started in milliseconds
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 5f48574dcb..6b22ed397e 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -128,8 +128,11 @@ void GfxView::initData(GuiResourceId resourceId) {
_palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
_embeddedPal = true;
} else {
- // Only use the EGA-mapping, when being SCI1
- if (getSciVersion() >= SCI_VERSION_1_EGA) {
+ // Only use the EGA-mapping, when being SCI1 EGA
+ // SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views
+ // with broken mapping tables. I guess those games won't use the mapping, so I rather disable it
+ // for them
+ 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)
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 238209c446..344eef76d4 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -67,6 +67,7 @@ MODULE_OBJS := \
sound/soundcmd.o \
sound/drivers/adlib.o \
sound/drivers/amigamac.o \
+ sound/drivers/cms.o \
sound/drivers/fb01.o \
sound/drivers/midi.o \
sound/drivers/pcjr.o \
diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp
index 6f37b49919..03e9d29660 100644
--- a/engines/sci/parser/grammar.cpp
+++ b/engines/sci/parser/grammar.cpp
@@ -38,8 +38,9 @@ namespace Sci {
#define TOKEN_CPAREN 0xfe000000
#define TOKEN_TERMINAL_CLASS 0x10000
#define TOKEN_TERMINAL_GROUP 0x20000
-#define TOKEN_STUFFING_WORD 0x40000
-#define TOKEN_NON_NT (TOKEN_OPAREN | TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP | TOKEN_STUFFING_WORD)
+#define TOKEN_STUFFING_LEAF 0x40000
+#define TOKEN_STUFFING_WORD 0x80000
+#define TOKEN_NON_NT (TOKEN_OPAREN | TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP | TOKEN_STUFFING_LEAF | TOKEN_STUFFING_WORD)
#define TOKEN_TERMINAL (TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP)
static int _allocd_rules = 0; // FIXME: Avoid non-const global vars
@@ -122,8 +123,10 @@ static void vocab_print_rule(ParseRule *rule) {
printf("C(%04x)", token & 0xffff);
else if (token & TOKEN_TERMINAL_GROUP)
printf("G(%04x)", token & 0xffff);
- else if (token & TOKEN_STUFFING_WORD)
+ else if (token & TOKEN_STUFFING_LEAF)
printf("%03x", token & 0xffff);
+ else if (token & TOKEN_STUFFING_WORD)
+ printf("{%03x}", token & 0xffff);
else
printf("[%03x]", token); /* non-terminal */
wspace = 1;
@@ -206,8 +209,8 @@ static ParseRule *_vbuild_rule(const parse_tree_branch_t *branch) {
rule->_data[tokens++] = value | TOKEN_STUFFING_WORD;
else { // normal inductive rule
rule->_data[tokens++] = TOKEN_OPAREN;
- rule->_data[tokens++] = type | TOKEN_STUFFING_WORD;
- rule->_data[tokens++] = value | TOKEN_STUFFING_WORD;
+ rule->_data[tokens++] = type | TOKEN_STUFFING_LEAF;
+ rule->_data[tokens++] = value | TOKEN_STUFFING_LEAF;
if (i == 0)
rule->_firstSpecial = tokens;
@@ -220,7 +223,7 @@ static ParseRule *_vbuild_rule(const parse_tree_branch_t *branch) {
return rule;
}
-static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWord &input) {
+static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWordList &input) {
int dep;
if (!rule->_numSpecials)
@@ -228,11 +231,32 @@ static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWord &input) {
dep = rule->_data[rule->_firstSpecial];
- if (((dep & TOKEN_TERMINAL_CLASS) && ((dep & 0xffff) & input._class)) ||
- ((dep & TOKEN_TERMINAL_GROUP) && ((dep & 0xffff) & input._group))) {
+ int count = 0;
+ int match = 0;
+ ResultWordList::const_iterator iter;
+ // TODO: Inserting an array in the middle of another array is slow
+ Common::Array<int> matches;
+ matches.reserve(input.size());
+
+ // We store the first match in 'match', and any subsequent matches in
+ // 'matches'. 'match' replaces the special in the rule, and 'matches' gets
+ // inserted after it.
+ for (iter = input.begin(); iter != input.end(); ++iter)
+ if (((dep & TOKEN_TERMINAL_CLASS) && ((dep & 0xffff) & iter->_class)) ||
+ ((dep & TOKEN_TERMINAL_GROUP) && ((dep & 0xffff) & iter->_group))) {
+ if (count == 0)
+ match = TOKEN_STUFFING_WORD | iter->_group;
+ else
+ matches.push_back(TOKEN_STUFFING_WORD | iter->_group);
+ count++;
+ }
+
+ if (count) {
ParseRule *retval = new ParseRule(*rule);
++_allocd_rules;
- retval->_data[rule->_firstSpecial] = TOKEN_STUFFING_WORD | input._group;
+ retval->_data[rule->_firstSpecial] = match;
+ if (count > 1)
+ retval->_data.insert_at(rule->_firstSpecial+1, matches);
retval->_numSpecials--;
retval->_firstSpecial = 0;
@@ -277,10 +301,8 @@ static ParseRuleList *_vocab_add_rule(ParseRuleList *list, ParseRule *rule) {
while (seeker->next/* && seeker->next->terminal <= term*/) {
if (seeker->next->terminal == term) {
if (*(seeker->next->rule) == *rule) {
- delete rule;
- // FIXME: not sure about this change, fixes pq2 crashing when having opened the cabinet
- // and typing "go to bains" - delete rule deletes part of new_elem
- //delete new_elem;
+ delete new_elem; // NB: This also deletes 'rule'
+
return list; // No duplicate rules
}
}
@@ -445,6 +467,7 @@ static int _vbpt_append(ParseTreeNode *nodes, int *pos, int base, int value) {
nodes[base].left = &nodes[++(*pos)];
nodes[*pos].type = kParseTreeLeafNode;
nodes[*pos].value = value;
+ nodes[*pos].right = 0;
nodes[base].right = &nodes[++(*pos)];
nodes[*pos].type = kParseTreeBranchNode;
nodes[*pos].left = 0;
@@ -456,9 +479,29 @@ static int _vbpt_terminate(ParseTreeNode *nodes, int *pos, int base, int value)
// Terminates, overwriting a nextwrite forknode
nodes[base].type = kParseTreeLeafNode;
nodes[base].value = value;
+ nodes[base].right = 0;
+ return *pos;
+}
+static int _vbpt_append_word(ParseTreeNode *nodes, int *pos, int base, int value) {
+ // writes one value to an existing node and creates a sibling for writing
+ nodes[base].type = kParseTreeWordNode;
+ nodes[base].value = value;
+ nodes[base].right = &nodes[++(*pos)];
+ nodes[*pos].type = kParseTreeBranchNode;
+ nodes[*pos].left = 0;
+ nodes[*pos].right = 0;
+ return *pos;
+}
+
+static int _vbpt_terminate_word(ParseTreeNode *nodes, int *pos, int base, int value) {
+ // Terminates, overwriting a nextwrite forknode
+ nodes[base].type = kParseTreeWordNode;
+ nodes[base].value = value;
+ nodes[base].right = 0;
return *pos;
}
+
static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) {
uint token;
@@ -470,11 +513,16 @@ static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *
nexttoken = (rulepos < rule->_data.size()) ? rule->_data[rulepos] : TOKEN_CPAREN;
if (nexttoken != TOKEN_CPAREN)
writepos = _vbpt_parenc(nodes, pos, writepos);
- } else if (token & TOKEN_STUFFING_WORD) {
+ } else if (token & TOKEN_STUFFING_LEAF) {
if (nexttoken == TOKEN_CPAREN)
writepos = _vbpt_terminate(nodes, pos, writepos, token & 0xffff);
else
writepos = _vbpt_append(nodes, pos, writepos, token & 0xffff);
+ } else if (token & TOKEN_STUFFING_WORD) {
+ if (nexttoken == TOKEN_CPAREN)
+ writepos = _vbpt_terminate_word(nodes, pos, writepos, token & 0xffff);
+ else
+ writepos = _vbpt_append_word(nodes, pos, writepos, token & 0xffff);
} else {
printf("\nError in parser (grammar.cpp, _vbpt_write_subexpression()): Rule data broken in rule ");
vocab_print_rule(rule);
@@ -486,16 +534,16 @@ static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *
return rulepos;
}
-int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) {
+int Vocabulary::parseGNF(const ResultWordListList &words, bool verbose) {
Console *con = g_sci->getSciDebugger();
// Get the start rules:
ParseRuleList *work = _vocab_clone_rule_list_by_id(_parserRules, _parserBranches[0].data[1]);
ParseRuleList *results = NULL;
uint word = 0;
const uint words_nr = words.size();
- ResultWordList::const_iterator word_iter = words.begin();
+ ResultWordListList::const_iterator words_iter;
- for (word_iter = words.begin(); word_iter != words.end(); ++word_iter, ++word) {
+ for (words_iter = words.begin(); words_iter != words.end(); ++words_iter, ++word) {
ParseRuleList *new_work = NULL;
ParseRuleList *reduced_rules = NULL;
ParseRuleList *seeker, *subseeker;
@@ -505,8 +553,9 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) {
seeker = work;
while (seeker) {
- if (seeker->rule->_numSpecials <= (words_nr - word))
- reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, *word_iter));
+ if (seeker->rule->_numSpecials <= (words_nr - word)) {
+ reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, *words_iter));
+ }
seeker = seeker->next;
}
@@ -570,6 +619,7 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) {
_parserNodes[1].type = kParseTreeLeafNode;
_parserNodes[1].value = 0x141;
+ _parserNodes[1].right = 0;
_parserNodes[2].type = kParseTreeBranchNode;
_parserNodes[2].left = 0;
diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp
index 9c07be2dff..7393874856 100644
--- a/engines/sci/parser/said.cpp
+++ b/engines/sci/parser/said.cpp
@@ -94,6 +94,7 @@ static ParseTreeNode* said_next_node() {
static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) {
pos->type = kParseTreeLeafNode;
pos->value = value;
+ pos->right = 0;
return pos;
}
@@ -101,6 +102,7 @@ static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) {
static ParseTreeNode* said_word_node(ParseTreeNode* pos, int value) {
pos->type = kParseTreeWordNode;
pos->value = value;
+ pos->right = 0;
return pos;
}
@@ -780,17 +782,39 @@ static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT)
// both saidT and parseT are terminals
int said_val = node_terminal_value(saidT);
- int parse_val = node_terminal_value(parseT);
- if (said_val != WORD_NONE &&
- (said_val == parse_val || said_val == WORD_ANY ||
- parse_val == WORD_ANY))
+#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION
+ scidprintf("%*smatchTrees matching terminals: %03x", outputDepth, "", node_terminal_value(parseT));
+ ParseTreeNode* t = parseT->right->right;
+ while (t) {
+ scidprintf(",%03x", t->value);
+ t = t->right;
+ }
+ scidprintf(" vs %03x", said_val);
+#endif
+
+ if (said_val == WORD_NONE) {
+ ret = -1;
+ } else if (said_val == WORD_ANY) {
ret = 1;
- else
+ } else {
ret = -1;
- scidprintf("%*smatchTrees matching terminals: %03x vs %03x (%d)\n",
- outputDepth, "", parse_val, said_val, ret);
+ // scan through the word group ids in the parse tree leaf to see if
+ // one matches the word group in the said tree
+ parseT = parseT->right->right;
+ do {
+ assert(parseT->type != kParseTreeBranchNode);
+ int parse_val = parseT->value;
+ if (parse_val == WORD_ANY || parse_val == said_val) {
+ ret = 1;
+ break;
+ }
+ parseT = parseT->right;
+ } while (parseT);
+ }
+
+ scidprintf(" (ret %d)\n", ret);
} else if (node_is_terminal(saidT) && !node_is_terminal(parseT)) {
@@ -1107,7 +1131,7 @@ True
said put washer on shaft & 455 , ( 3fa < cb ) / 8c6
True
-said depth correct & [!*] < 8b1 / 22
+said depth correct & [!*] < 8b1 / 22b
True
said depth acknowledged & / 46d , 460 , 44d < 8b1
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 20436d5b30..f9989b22a8 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -40,6 +40,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
// Mark parse tree as unused
_parserNodes[0].type = kParseTreeLeafNode;
_parserNodes[0].value = 0;
+ _parserNodes[0].right = 0;
_synonyms.clear(); // No synonyms
@@ -72,6 +73,8 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
_parserRules = NULL;
}
+ loadAltInputs();
+
parser_base = NULL_REG;
parser_event = NULL_REG;
parserIsValid = false;
@@ -80,6 +83,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan),
Vocabulary::~Vocabulary() {
freeRuleList(_parserRules);
freeSuffixes();
+ freeAltInputs();
}
void Vocabulary::reset() {
@@ -165,8 +169,14 @@ bool Vocabulary::loadParserWords() {
newWord._class = ((resource->data[seeker]) << 4) | ((c & 0xf0) >> 4);
newWord._group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8);
- // Add the word to the list
- _parserWords[currentWord] = newWord;
+ // SCI01 was the first version to support multiple class/group pairs
+ // per word, so we clear the list in earlier versions
+ // in earlier versions.
+ if (getSciVersion() < SCI_VERSION_01)
+ _parserWords[currentWord].clear();
+
+ // Add this to the list of possible class,group pairs for this word
+ _parserWords[currentWord].push_back(newWord);
seeker += 3;
}
@@ -181,8 +191,9 @@ const char *Vocabulary::getAnyWordFromGroup(int group) {
return "{nothing}";
for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) {
- if (i->_value._group == group)
- return i->_key.c_str();
+ for (ResultWordList::const_iterator j = i->_value.begin(); j != i->_value.end(); ++j)
+ if (j->_group == group)
+ return i->_key.c_str();
}
return "{invalid}";
@@ -264,8 +275,108 @@ bool Vocabulary::loadBranches() {
return true;
}
+bool Vocabulary::loadAltInputs() {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 1);
+
+ if (!resource)
+ return true; // it's not a problem if this resource doesn't exist
+
+ const char *data = (const char*)resource->data;
+ const char *data_end = data + resource->size;
+
+ _altInputs.clear();
+ _altInputs.resize(256);
+
+ while (data < data_end && *data) {
+ AltInput t;
+ t._input = data;
+
+ unsigned int l = strlen(data);
+ t._inputLength = l;
+ data += l + 1;
+
+ t._replacement = data;
+ l = strlen(data);
+ data += l + 1;
+
+ if (data < data_end && strncmp(data, t._input, t._inputLength) == 0)
+ t._prefix = true;
+ else
+ t._prefix = false;
+
+ unsigned char firstChar = t._input[0];
+ _altInputs[firstChar].push_front(t);
+ }
+
+ return true;
+}
+
+void Vocabulary::freeAltInputs() {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 0);
+ if (resource)
+ _resMan->unlockResource(resource);
+
+ _altInputs.clear();
+}
+
+bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) {
+ if (_altInputs.empty())
+ return false;
+ if (SELECTOR(parseLang) == -1)
+ return false;
+ if (readSelectorValue(g_sci->getEngineState()->_segMan, g_sci->getGameObject(), SELECTOR(parseLang)) == 1)
+ return false;
+
+ bool ret = false;
+ unsigned int loopCount = 0;
+ bool changed;
+ do {
+ changed = false;
+
+ const char* t = text.c_str();
+ unsigned int tlen = text.size();
+
+ for (unsigned int p = 0; p < tlen && !changed; ++p) {
+ unsigned char s = t[p];
+ if (s >= _altInputs.size() || _altInputs[s].empty())
+ continue;
+ Common::List<AltInput>::iterator i;
+ for (i = _altInputs[s].begin(); i != _altInputs[s].end(); ++i) {
+ if (p + i->_inputLength > tlen)
+ continue;
+ if (i->_prefix && cursorPos > p && cursorPos <= p + i->_inputLength)
+ continue;
+ if (strncmp(i->_input, t+p, i->_inputLength) == 0) {
+ // replace
+ if (cursorPos > p + i->_inputLength) {
+ cursorPos += strlen(i->_replacement) - i->_inputLength;
+ } else if (cursorPos > p) {
+ cursorPos = p + strlen(i->_replacement);
+ }
+
+ for (unsigned int j = 0; j < i->_inputLength; ++j)
+ text.deleteChar(p);
+ const char *r = i->_replacement;
+ while (*r)
+ text.insertChar(*r++, p++);
+
+ assert(cursorPos <= text.size());
+
+ changed = true;
+ ret = true;
+ break;
+ }
+ }
+ }
+ } while (changed && loopCount < 10);
+
+ return ret;
+}
+
// we assume that *word points to an already lowercased word
-ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
+void Vocabulary::lookupWord(ResultWordList& retval, const char *word, int word_len) {
+ retval.clear();
+
Common::String tempword(word, word_len);
// Remove all dashes from tempword
@@ -277,15 +388,22 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
}
// Look it up:
- WordMap::iterator dict_word = _parserWords.find(tempword);
+ WordMap::iterator dict_words = _parserWords.find(tempword);
// Match found? Return it!
- if (dict_word != _parserWords.end()) {
- return dict_word->_value;
+ if (dict_words != _parserWords.end()) {
+ retval = dict_words->_value;
+
+ // SCI01 was the first version to support
+ // multiple matches, so no need to look further
+ // in earlier versions.
+ if (getSciVersion() < SCI_VERSION_01)
+ return;
+
}
// Now try all suffixes
- for (SuffixList::const_iterator suffix = _parserSuffixes.begin(); suffix != _parserSuffixes.end(); ++suffix)
+ for (SuffixList::const_iterator suffix = _parserSuffixes.begin(); suffix != _parserSuffixes.end(); ++suffix) {
if (suffix->alt_suffix_length <= word_len) {
int suff_index = word_len - suffix->alt_suffix_length;
@@ -298,27 +416,38 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) {
// ...and append "correct" suffix
tempword2 += Common::String(suffix->word_suffix, suffix->word_suffix_length);
- dict_word = _parserWords.find(tempword2);
-
- if ((dict_word != _parserWords.end()) && (dict_word->_value._class & suffix->class_mask)) { // Found it?
- // Use suffix class
- ResultWord tmp = dict_word->_value;
- tmp._class = suffix->result_class;
- return tmp;
+ dict_words = _parserWords.find(tempword2);
+
+ if (dict_words != _parserWords.end()) {
+ for (ResultWordList::const_iterator j = dict_words->_value.begin(); j != dict_words->_value.end(); ++j) {
+ if (j->_class & suffix->class_mask) { // Found it?
+ // Use suffix class
+ ResultWord tmp = *j;
+ tmp._class = suffix->result_class;
+ retval.push_back(tmp);
+
+ // SCI01 was the first version to support
+ // multiple matches, so no need to look further
+ // in earlier versions.
+ if (getSciVersion() < SCI_VERSION_01)
+ return;
+ }
+ }
}
}
}
+ }
+
+ if (!retval.empty())
+ return;
// No match so far? Check if it's a number.
- ResultWord retval = { -1, -1 };
char *tester;
if ((strtol(tempword.c_str(), &tester, 10) >= 0) && (*tester == '\0')) { // Do we have a complete number here?
ResultWord tmp = { VOCAB_CLASS_NUMBER, VOCAB_MAGIC_NUMBER_GROUP };
- retval = tmp;
+ retval.push_back(tmp);
}
-
- return retval;
}
void Vocabulary::debugDecipherSaidBlock(const byte *addr) {
@@ -397,7 +526,7 @@ static const byte lowerCaseMap[256] = {
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) {
+bool Vocabulary::tokenizeString(ResultWordListList &retval, const char *sentence, char **error) {
char currentWord[VOCAB_MAX_WORDLENGTH] = "";
int pos_in_sentence = 0;
unsigned char c;
@@ -418,10 +547,12 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch
else {
if (wordLen) { // Finished a word?
- ResultWord lookup_result = lookupWord(currentWord, wordLen);
+ ResultWordList lookup_result;
+
// Look it up
+ lookupWord(lookup_result, currentWord, wordLen);
- if (lookup_result._class == -1) { // Not found?
+ if (lookup_result.empty()) { // Not found?
*error = (char *)calloc(wordLen + 1, 1);
strncpy(*error, currentWord, wordLen); // Set the offending word
retval.clear();
@@ -459,43 +590,19 @@ void Vocabulary::printSuffixes() const {
void Vocabulary::printParserWords() const {
Console *con = g_sci->getSciDebugger();
- int j = 0;
+ int n = 0;
for (WordMap::iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) {
- con->DebugPrintf("%4d: %03x [%03x] %20s |", j, i->_value._class, i->_value._group, i->_key.c_str());
- if (j % 3 == 0)
- con->DebugPrintf("\n");
- j++;
+ for (ResultWordList::iterator j = i->_value.begin(); j != i->_value.end(); ++j) {
+ con->DebugPrintf("%4d: %03x [%03x] %20s |", n, j->_class, j->_group, i->_key.c_str());
+ if (n % 3 == 0)
+ con->DebugPrintf("\n");
+ n++;
+ }
}
con->DebugPrintf("\n");
}
-void _vocab_recursive_ptree_dump_treelike(ParseTreeNode *tree) {
- assert(tree);
-
- if (tree->type == kParseTreeLeafNode)
- printf("%x", tree->value);
- else {
- ParseTreeNode* lbranch = tree->left;
- ParseTreeNode* rbranch = tree->right;
- printf("<");
-
- if (lbranch)
- _vocab_recursive_ptree_dump_treelike(lbranch);
- else
- printf("NULL");
-
- printf(",");
-
- if (rbranch)
- _vocab_recursive_ptree_dump_treelike(rbranch);
- else
- printf("NULL");
-
- printf(">");
- }
-}
-
void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) {
assert(tree);
@@ -526,33 +633,37 @@ void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) {
if (rbranch) {
if (rbranch->type == kParseTreeBranchNode)
_vocab_recursive_ptree_dump(rbranch, blanks);
- else
+ else {
printf("%x", rbranch->value);
+ while (rbranch->right) {
+ rbranch = rbranch->right;
+ printf("/%x", rbranch->value);
+ }
+ }
}/* else printf("nil");*/
}
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, 1);
printf("))\n");
}
void Vocabulary::dumpParseTree() {
- //_vocab_recursive_ptree_dump_treelike(nodes, 0, 0);
printf("(setq parse-tree \n'(");
_vocab_recursive_ptree_dump(_parserNodes, 1);
printf("))\n");
}
-void Vocabulary::synonymizeTokens(ResultWordList &words) {
+void Vocabulary::synonymizeTokens(ResultWordListList &words) {
if (_synonyms.empty())
return; // No synonyms: Nothing to check
- for (ResultWordList::iterator i = words.begin(); i != words.end(); ++i)
- for (SynonymList::const_iterator sync = _synonyms.begin(); sync != _synonyms.end(); ++sync)
- if (i->_group == sync->replaceant)
- i->_group = sync->replacement;
+ for (ResultWordListList::iterator i = words.begin(); i != words.end(); ++i)
+ for (ResultWordList::iterator j = i->begin(); j != i->end(); ++j)
+ for (SynonymList::const_iterator sync = _synonyms.begin(); sync != _synonyms.end(); ++sync)
+ if (j->_group == sync->replaceant)
+ j->_group = sync->replacement;
}
void Vocabulary::printParserNodes(int num) {
@@ -578,6 +689,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].value = nr;
+ _parserNodes[*pos].right = 0;
return *pos;
}
if (type == kParseEndOfInput) {
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index d4df8af715..620d50c09d 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -49,7 +49,9 @@ enum {
VOCAB_RESOURCE_SCI1_MAIN_VOCAB = 900,
VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES = 901,
- VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902
+ VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902,
+
+ VOCAB_RESOURCE_ALT_INPUTS = 913
};
@@ -117,8 +119,9 @@ struct ResultWord {
};
typedef Common::List<ResultWord> ResultWordList;
+typedef Common::List<ResultWordList> ResultWordListList;
-typedef Common::HashMap<Common::String, ResultWord, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap;
+typedef Common::HashMap<Common::String, ResultWordList, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap;
struct ParseRuleList;
@@ -146,6 +149,15 @@ struct synonym_t {
typedef Common::List<synonym_t> SynonymList;
+
+struct AltInput {
+ const char *_input;
+ const char *_replacement;
+ unsigned int _inputLength;
+ bool _prefix;
+};
+
+
struct parse_tree_branch_t {
int id;
int data[10];
@@ -161,7 +173,7 @@ struct ParseTreeNode {
ParseTypes type; /**< leaf or branch */
int value; /**< For leaves */
ParseTreeNode* left; /**< Left child, for branches */
- ParseTreeNode* right; /**< Right child, for branches */
+ ParseTreeNode* right; /**< Right child, for branches (and word leaves) */
};
enum VocabularyVersions {
@@ -186,11 +198,11 @@ public:
/**
* Looks up a single word in the words and suffixes list.
+ * @param retval the list of matches
* @param word pointer to the word to look up
* @param word_len length of the word to look up
- * @return the matching word (or (-1,-1) if there was no match)
*/
- ResultWord lookupWord(const char *word, int word_len);
+ void lookupWord(ResultWordList &retval, const char *word, int word_len);
/**
@@ -204,7 +216,7 @@ public:
* contain any useful words; if not, *error points to a malloc'd copy of
* the offending word. The returned list may contain anywords.
*/
- bool tokenizeString(ResultWordList &retval, const char *sentence, char **error);
+ bool tokenizeString(ResultWordListList &retval, const char *sentence, char **error);
/**
* Builds a parse tree from a list of words, using a set of Greibach Normal
@@ -215,7 +227,7 @@ public:
* nodes or if the sentence structure in 'words' is not part of the
* language described by the grammar passed in 'rules'.
*/
- int parseGNF(const ResultWordList &words, bool verbose = false);
+ int parseGNF(const ResultWordListList &words, bool verbose = false);
/**
* Constructs the Greibach Normal Form of the grammar supplied in 'branches'.
@@ -262,9 +274,9 @@ public:
/**
* Synonymizes a token list
- * Parameters: (ResultWordList &) words: The word list to synonymize
+ * Parameters: (ResultWordListList &) words: The word list to synonymize
*/
- void synonymizeTokens(ResultWordList &words);
+ void synonymizeTokens(ResultWordListList &words);
void printParserNodes(int num);
@@ -272,6 +284,14 @@ public:
int parseNodes(int *i, int *pos, int type, int nr, int argc, const char **argv);
+ /**
+ * Check text input against alternative inputs.
+ * @param text The text to process. It will be modified in-place
+ * @param cursorPos The cursor position
+ * @return true if anything changed
+ */
+ bool checkAltInput(Common::String& text, uint16& cursorPos);
+
private:
/**
* Loads all words from the main vocabulary.
@@ -304,6 +324,20 @@ private:
*/
void freeRuleList(ParseRuleList *rule_list);
+
+ /**
+ * Retrieves all alternative input combinations from vocab 913.
+ * @return true on success, false on error
+ */
+ bool loadAltInputs();
+
+ /**
+ * Frees all alternative input combinations.
+ */
+ void freeAltInputs();
+
+
+
ResourceManager *_resMan;
VocabularyVersions _vocabVersion;
@@ -318,6 +352,7 @@ private:
Common::Array<parse_tree_branch_t> _parserBranches;
WordMap _parserWords;
SynonymList _synonyms; /**< The list of synonyms */
+ Common::Array<Common::List<AltInput> > _altInputs;
public:
// Accessed by said()
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 00f50714af..9809f10576 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -92,7 +92,7 @@ const char *getSciVersionDesc(SciVersion version) {
#undef SCI_REQUIRE_RESOURCE_FILES
-//#define SCI_VERBOSE_resMan 1
+//#define SCI_VERBOSE_RESMAN 1
static const char *sci_error_types[] = {
"No error",
@@ -142,7 +142,6 @@ static const ResourceType s_resTypeMapSci0[] = {
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
@@ -152,7 +151,6 @@ static const ResourceType s_resTypeMapSci21[] = {
kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13
kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD // 0x14-0x17
};
-#endif
ResourceType ResourceManager::convertResType(byte type) {
type &= 0x7f;
@@ -163,7 +161,6 @@ ResourceType ResourceManager::convertResType(byte type) {
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
@@ -173,9 +170,6 @@ ResourceType ResourceManager::convertResType(byte type) {
else
return s_resTypeMapSci21[type];
}
-#else
- error("SCI32 support not compiled in");
-#endif
}
return kResourceTypeInvalid;
@@ -490,8 +484,9 @@ void ResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
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]);
+ warning("Error %d occurred while reading %s from resource file %s: %s",
+ error, res->_id.toString().c_str(), res->getResourceLocation().c_str(),
+ sci_error_types[error]);
res->unalloc();
}
@@ -852,7 +847,16 @@ void ResourceManager::init() {
debugC(1, kDebugLevelResMan, "resMan: Detected Amiga graphic resources");
break;
default:
+#ifdef ENABLE_SCI32
error("resMan: Couldn't determine view type");
+#else
+ if (getSciVersion() >= SCI_VERSION_2) {
+ // SCI support isn't built in, thus the view type won't be determined for
+ // SCI2+ games. This will be handled further up, so throw no error here
+ } else {
+ error("resMan: Couldn't determine view type");
+ }
+#endif
}
#ifdef ENABLE_SCI32
@@ -904,7 +908,7 @@ void ResourceManager::addToLRU(Resource *res) {
}
_LRU.push_front(res);
_memoryLRU += res->size;
-#if SCI_VERBOSE_resMan
+#if SCI_VERBOSE_RESMAN
debug("Adding %s.%03d (%d bytes) to lru control: %d bytes total",
getResourceTypeName(res->type), res->number, res->size,
mgr->_memoryLRU);
@@ -935,7 +939,7 @@ void ResourceManager::freeOldResources() {
Resource *goner = *_LRU.reverse_begin();
removeFromLRU(goner);
goner->unalloc();
-#ifdef SCI_VERBOSE_resMan
+#ifdef SCI_VERBOSE_RESMAN
printf("resMan-debug: LRU: Freeing %s.%03d (%d bytes)\n", getResourceTypeName(goner->type), goner->number, goner->size);
#endif
}
@@ -1518,7 +1522,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
} while (type != 0x1F); // the last entry is FF
// reading each type's offsets
- uint32 off = 0;
+ uint32 fileOffset = 0;
for (type = 0; type < 32; type++) {
if (resMap[type].wOffset == 0) // this resource does not exist in map
continue;
@@ -1528,15 +1532,15 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
int volume_nr = 0;
if (_mapVersion == kResVersionSci11) {
// offset stored in 3 bytes
- off = fileStream->readUint16LE();
- off |= fileStream->readByte() << 16;
- off <<= 1;
+ fileOffset = fileStream->readUint16LE();
+ fileOffset |= fileStream->readByte() << 16;
+ fileOffset <<= 1;
} else {
// offset/volume stored in 4 bytes
- off = fileStream->readUint32LE();
+ fileOffset = fileStream->readUint32LE();
if (_mapVersion < kResVersionSci11) {
- volume_nr = off >> 28; // most significant 4 bits
- off &= 0x0FFFFFFF; // least significant 28 bits
+ volume_nr = fileOffset >> 28; // most significant 4 bits
+ fileOffset &= 0x0FFFFFFF; // least significant 28 bits
} else {
// in SCI32 it's a plain offset
}
@@ -1547,19 +1551,30 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
return SCI_ERROR_RESMAP_NOT_FOUND;
}
resId = ResourceId(convertResType(type), number);
- // adding new resource only if it does not exist
- if (_resMap.contains(resId) == false) {
- // 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
- // need to be used in concurrence with the volume specified in the map to get
- // the actual resource file.
- 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);
+ // 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
+ // need to be used in concurrence with the volume specified in the map to get
+ // the actual resource file.
+ 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);
+
+ Resource *resource = _resMap.getVal(resId, NULL);
+ if (!resource) {
+ addResource(resId, source, fileOffset);
+ } else {
+ // if resource is already present, change it to new content
+ // this is needed at least for pharkas/german. This version
+ // contains several duplicate resources INSIDE the resource
+ // data files like fonts, views, scripts, etc. And if we use
+ // the first entries, half of the game will be english and
+ // umlauts will also be missing :P
+ resource->_source = source;
+ resource->_fileOffset = fileOffset;
+ resource->size = 0;
}
}
}
@@ -1934,7 +1949,18 @@ void ResourceManager::detectSciVersion() {
s_sciVersion = SCI_VERSION_0_EARLY;
bool oldDecompressors = true;
- ResourceCompression viewCompression = getViewCompression();
+ ResourceCompression viewCompression;
+#ifdef ENABLE_SCI32
+ viewCompression = getViewCompression();
+#else
+ if (_volVersion == kResVersionSci32) {
+ // SCI32 support isn't built in, thus view detection will fail
+ viewCompression = kCompUnknown;
+ } else {
+ viewCompression = getViewCompression();
+ }
+#endif
+
if (viewCompression != kCompLZW) {
// If it's a different compression type from kCompLZW, the game is probably
// SCI_VERSION_1_EGA or later. If the views are uncompressed, it is
@@ -1955,8 +1981,18 @@ void ResourceManager::detectSciVersion() {
// SCI1.1 VGA views
_viewType = kViewVga11;
} else {
+#ifdef ENABLE_SCI32
// Otherwise we detect it from a view
_viewType = detectViewType();
+#else
+ if (_volVersion == kResVersionSci32 && viewCompression == kCompUnknown) {
+ // A SCI32 game, but SCI32 support is disabled. Force the view type
+ // to kViewVga11, as we can't read from the game's resource files
+ _viewType = kViewVga11;
+ } else {
+ _viewType = detectViewType();
+ }
+#endif
}
if (_volVersion == kResVersionSci11Mac) {
@@ -2062,7 +2098,7 @@ void ResourceManager::detectSciVersion() {
// is increment here, but ignore for all the regular sci1late games
// the problem is, we dont have access to that detection till later
// so maybe (part of?) that detection should get moved in here
- if ((g_sci->getGameId() == GID_LSL1) && (g_sci->getLanguage() == Common::ES_ESP)) {
+ if (g_sci && (g_sci->getGameId() == GID_LSL1) && (g_sci->getLanguage() == Common::ES_ESP)) {
s_sciVersion = SCI_VERSION_1_MIDDLE;
return;
}
@@ -2137,6 +2173,19 @@ bool ResourceManager::detectForPaletteMergingForSci11() {
return false;
}
+// is called on SCI0EARLY games to make sure that sound resources are in fact also SCI0EARLY
+bool ResourceManager::detectEarlySound() {
+ Resource *res = findResource(ResourceId(kResourceTypeSound, 1), 0);
+ if (res) {
+ if (res->size >= 0x22) {
+ if (READ_LE_UINT16(res->data + 0x1f) == 0) // channel 15 voice count + play mask is 0 in SCI0LATE
+ if (res->data[0x21] == 0) // last byte right before actual data is 0 as well
+ return false;
+ }
+ }
+ return true;
+}
+
// Functions below are based on PD code by Brian Provinciano (SCI Studio)
bool ResourceManager::hasOldScriptHeader() {
Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
@@ -2329,4 +2378,8 @@ Common::String ResourceManager::findSierraGameId() {
return sierraId;
}
+const Common::String &Resource::getResourceLocation() const {
+ return _source->getLocationName();
+}
+
} // End of namespace Sci
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 48210b835f..f5d6517398 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -219,6 +219,8 @@ public:
*/
void writeToStream(Common::WriteStream *stream) const;
+ const Common::String &getResourceLocation() const;
+
// 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.
@@ -315,6 +317,7 @@ public:
void setAudioLanguage(int language);
int getAudioLanguage() const;
+ bool isGMTrackIncluded();
bool isVGA() const { return (_viewType == kViewVga) || (_viewType == kViewVga11); }
bool isAmiga32color() const { return _viewType == kViewAmiga; }
bool isSci11Mac() const { return _volVersion == kResVersionSci11Mac; }
@@ -344,6 +347,8 @@ public:
bool detectFontExtended();
// Detects, if SCI1.1 game uses palette merging
bool detectForPaletteMergingForSci11();
+ // Detects, if SCI0EARLY game also has SCI0EARLY sound resources
+ bool detectEarlySound();
/**
* Finds the internal Sierra ID of the current game from script 0.
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index a25505fe47..e6b8fd06c2 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -61,7 +61,7 @@ AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, co
// 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!");
+ 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++) {
@@ -273,6 +273,13 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// w syncAscSize (iff seq has bit 6 set)
int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
+#ifndef ENABLE_SCI32
+ // SCI32 support is not built in. Check if this is a SCI32 game
+ // and if it is abort here.
+ if (_volVersion == kResVersionSci32)
+ return SCI_ERROR_RESMAP_NOT_FOUND;
+#endif
+
uint32 offset = 0;
Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
@@ -519,6 +526,41 @@ int ResourceManager::getAudioLanguage() const {
return (_audioMapSCI1 ? _audioMapSCI1->_volumeNumber : 0);
}
+bool ResourceManager::isGMTrackIncluded() {
+ // This check only makes sense for SCI1 and newer games
+ if (getSciVersion() < SCI_VERSION_1_EARLY)
+ return false;
+
+ // SCI2 and newer games always have GM tracks
+ if (getSciVersion() >= SCI_VERSION_2)
+ return true;
+
+ // For the leftover games, we can safely use SCI_VERSION_1_EARLY for the soundVersion
+ const SciVersion soundVersion = SCI_VERSION_1_EARLY;
+
+ // Read the first song and check if it has a GM track
+ bool result = false;
+ Common::List<ResourceId> *resources = listResources(kResourceTypeSound, -1);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+ int firstSongId = itr->getNumber();
+ delete resources;
+
+ SoundResource *song1 = new SoundResource(firstSongId, this, soundVersion);
+ if (!song1) {
+ warning("ResourceManager::isGMTrackIncluded: track 1 not found");
+ return false;
+ }
+
+ SoundResource::Track *gmTrack = song1->getTrackByType(0x07);
+ if (gmTrack)
+ result = true;
+
+ delete song1;
+
+ return result;
+}
+
SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resourceNr), true);
int trackNr, channelNr;
@@ -629,6 +671,8 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
channel->data = resource->data + dataOffset;
channel->size = READ_LE_UINT16(data + 4);
channel->curPos = 0;
+ // FIXME: number contains (low nibble) channel and (high nibble) flags
+ // 0x20 is set on rhythm channels to prevent remapping
channel->number = *channel->data;
channel->poly = *(channel->data + 1);
channel->time = channel->prev = 0;
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 7a9a786121..3fe398f426 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -45,6 +45,7 @@
#include "sci/engine/selector.h" // for SELECTOR
#include "sci/sound/audio.h"
+#include "sci/sound/music.h"
#include "sci/sound/soundcmd.h"
#include "sci/graphics/animate.h"
#include "sci/graphics/cache.h"
@@ -173,12 +174,15 @@ SciEngine::~SciEngine() {
g_sci = 0;
}
+extern void showScummVMDialog(const Common::String &message);
+
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");
+ ConfMan.registerDefault("sci_undither", "true");
+ ConfMan.registerDefault("sci_originalsaveload", "false");
+ ConfMan.registerDefault("native_fb01", "false");
_resMan = new ResourceManager();
assert(_resMan);
@@ -199,17 +203,19 @@ Common::Error SciEngine::run() {
// Add the after market GM patches for the specified game, if they exist
_resMan->addNewGMPatch(_gameId);
- _gameObj = _resMan->findGameObject();
+ _gameObjectAddress = _resMan->findGameObject();
+ _gameSuperClassAddress = NULL_REG;
SegManager *segMan = new SegManager(_resMan);
// Initialize the game screen
_gfxScreen = new GfxScreen(_resMan);
- _gfxScreen->debugUnditherSetState(ConfMan.getBool("undither"));
+ _gfxScreen->debugUnditherSetState(ConfMan.getBool("sci_undither"));
// 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;
@@ -229,6 +235,14 @@ Common::Error SciEngine::run() {
return Common::kUnknownError;
}
+ // we try to find the super class address of the game object, we can't do that earlier
+ const Object *gameObject = segMan->getObject(_gameObjectAddress);
+ if (!gameObject) {
+ warning("Could not get game object, aborting...");
+ return Common::kUnknownError;
+ }
+ _gameSuperClassAddress = gameObject->getSuperClassSelector();
+
script_adjust_opcode_formats();
// Must be called after game_init(), as they use _features
@@ -242,52 +256,93 @@ Common::Error SciEngine::run() {
debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
+ // Patch in our save/restore code, so that dialogs are replaced
+ patchGameSaveRestore(segMan);
+
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);
+ writeSelectorValue(segMan, _gameObjectAddress, SELECTOR(printLang), 1);
if (SELECTOR(parseLang) != -1) // and set parser language to english as well
- writeSelectorValue(segMan, _gameObj, SELECTOR(parseLang), 1);
+ writeSelectorValue(segMan, _gameObjectAddress, SELECTOR(parseLang), 1);
}
}
// 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)
+ int directSaveSlotLoading = ConfMan.getInt("save_slot");
+ if (directSaveSlotLoading >= 0) {
+ // call GameObject::play (like normally)
+ initStackBaseWithSelector(SELECTOR(play));
+ // We set this, so that the game automatically quit right after init
+ _gamestate->variables[VAR_GLOBAL][4] = TRUE_REG;
+
+ _gamestate->_executionStackPosChanged = false;
+ run_vm(_gamestate);
+
+ // As soon as we get control again, actually restore the game
+ reg_t restoreArgv[2] = { NULL_REG, make_reg(0, directSaveSlotLoading) }; // 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
+ // this indirectly calls GameObject::init, which will setup menu, text font/color codes etc.
+ // without this games would be pretty badly broken
+ }
- // 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);
+ // Show any special warnings for buggy scripts with severe game bugs,
+ // which have been patched by Sierra
+ if (getGameId() == GID_LONGBOW) {
+ // Longbow 1.0 has a buggy script which prevents the game
+ // from progressing during the Green Man riddle sequence.
+ // A patch for this buggy script has been released by Sierra,
+ // and is necessary to complete the game without issues.
+ // The patched script is included in Longbow 1.1.
+ // Refer to bug #3036609.
+ Resource *buggyScript = _resMan->findResource(ResourceId(kResourceTypeScript, 180), 0);
+
+ if (buggyScript && (buggyScript->size == 12354 || buggyScript->size == 12362)) {
+ showScummVMDialog("A known buggy game script has been detected, which could "
+ "prevent you from progressing later on in the game, during "
+ "the sequence with the Green Man's riddles. Please, apply "
+ "the latest patch for this game by Sierra to avoid possible "
+ "problems");
}
+ }
- // 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;
+ // Show a warning if the user has selected a General MIDI device, no GM patch exists
+ // (i.e. patch 4) and the game is one of the known 8 SCI1 games that Sierra has provided
+ // after market patches for in their "General MIDI Utility".
+ if (_soundCmd->getMusicType() == MT_GM && !ConfMan.getBool("native_mt32")) {
+ if (!_resMan->findResource(ResourceId(kResourceTypePatch, 4), 0)) {
+ switch (getGameId()) {
+ case GID_ECOQUEST:
+ case GID_HOYLE3:
+ case GID_LSL1:
+ case GID_LSL5:
+ case GID_LONGBOW:
+ case GID_SQ1:
+ case GID_SQ4:
+ case GID_FAIRYTALES:
+ showScummVMDialog("You have selected General MIDI as a sound device. Sierra "
+ "has provided after-market support for General MIDI for this "
+ "game in their \"General MIDI Utility\". Please, apply this "
+ "patch in order to enjoy MIDI music with this game. Once you "
+ "have obtained it, you can unpack all of the included *.PAT "
+ "files in your ScummVM extras folder and ScummVM will add the "
+ "appropriate patch automatically. Alternatively, you can follow "
+ "the instructions in the READ.ME file included in the patch and "
+ "rename the associated *.PAT file to 4.PAT and place it in the "
+ "game folder. Without this patch, General MIDI music for this "
+ "game will sound badly distorted.");
+ break;
+ default:
+ break;
+ }
}
}
+
runGame();
ConfMan.flushToDisk();
@@ -295,6 +350,101 @@ Common::Error SciEngine::run() {
return Common::kNoError;
}
+static byte patchGameRestoreSave[] = {
+ 0x39, 0x03, // pushi 03
+ 0x76, // push0
+ 0x38, 0xff, 0xff, // pushi -1
+ 0x76, // push0
+ 0x43, 0xff, 0x06, // call kRestoreGame/kSaveGame (will get fixed directly)
+ 0x48, // ret
+};
+
+void SciEngine::patchGameSaveRestore(SegManager *segMan) {
+ const Object *gameObject = segMan->getObject(_gameObjectAddress);
+ const uint16 gameMethodCount = gameObject->getMethodCount();
+ const Object *gameSuperObject = segMan->getObject(_gameSuperClassAddress);
+ const uint16 gameSuperMethodCount = gameSuperObject->getMethodCount();
+ reg_t methodAddress;
+ const uint16 kernelCount = _kernel->getKernelNamesSize();
+ const byte *scriptRestorePtr = NULL;
+ byte kernelIdRestore = 0;
+ const byte *scriptSavePtr = NULL;
+ byte kernelIdSave = 0;
+
+ // this feature is currently not supported on SCI32
+ if (getSciVersion() >= SCI_VERSION_2)
+ return;
+
+ switch (_gameId) {
+ case GID_MOTHERGOOSE256: // mother goose saves/restores directly and has no save/restore dialogs
+ case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
+ case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all
+ case GID_HOYLE2: // gets confused, see hoyle1
+ return;
+ default:
+ break;
+ }
+
+ if (ConfMan.getBool("sci_originalsaveload"))
+ return;
+
+ for (uint16 kernelNr = 0; kernelNr < kernelCount; kernelNr++) {
+ Common::String kernelName = _kernel->getKernelName(kernelNr);
+ if (kernelName == "RestoreGame")
+ kernelIdRestore = kernelNr;
+ if (kernelName == "SaveGame")
+ kernelIdSave = kernelNr;
+ }
+
+ // Search for gameobject-superclass ::restore
+ for (uint16 methodNr = 0; methodNr < gameSuperMethodCount; methodNr++) {
+ uint16 selectorId = gameSuperObject->getFuncSelector(methodNr);
+ Common::String methodName = _kernel->getSelectorName(selectorId);
+ if (methodName == "restore") {
+ methodAddress = gameSuperObject->getFunction(methodNr);
+ Script *script = segMan->getScript(methodAddress.segment);
+ scriptRestorePtr = script->getBuf(methodAddress.offset);
+ }
+ if (methodName == "save") {
+ methodAddress = gameSuperObject->getFunction(methodNr);
+ Script *script = segMan->getScript(methodAddress.segment);
+ scriptSavePtr = script->getBuf(methodAddress.offset);
+ }
+ }
+
+ // Search for gameobject ::save, if there is one patch that one instead
+ for (uint16 methodNr = 0; methodNr < gameMethodCount; methodNr++) {
+ uint16 selectorId = gameObject->getFuncSelector(methodNr);
+ Common::String methodName = _kernel->getSelectorName(selectorId);
+ if (methodName == "save") {
+ methodAddress = gameObject->getFunction(methodNr);
+ Script *script = segMan->getScript(methodAddress.segment);
+ scriptSavePtr = script->getBuf(methodAddress.offset);
+ break;
+ }
+ }
+
+ switch (_gameId) {
+ case GID_FAIRYTALES: // fairy tales automatically saves w/o dialog
+ scriptSavePtr = NULL;
+ default:
+ break;
+ }
+
+ if (scriptRestorePtr) {
+ // Now patch in our code
+ byte *patchPtr = const_cast<byte *>(scriptRestorePtr);
+ memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
+ patchPtr[8] = kernelIdRestore;
+ }
+ if (scriptSavePtr) {
+ // Now patch in our code
+ byte *patchPtr = const_cast<byte *>(scriptSavePtr);
+ memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
+ patchPtr[8] = kernelIdSave;
+ }
+}
+
bool SciEngine::initGame() {
// Script 0 needs to be allocated here before anything else!
int script0Segment = _gamestate->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
@@ -422,8 +572,8 @@ void SciEngine::initStackBaseWithSelector(Selector selector) {
_gamestate->stack_base[1] = NULL_REG;
// Register the first element on the execution stack
- if (!send_selector(_gamestate, _gameObj, _gameObj, _gamestate->stack_base, 2, _gamestate->stack_base)) {
- _console->printObject(_gameObj);
+ if (!send_selector(_gamestate, _gameObjectAddress, _gameObjectAddress, _gamestate->stack_base, 2, _gamestate->stack_base)) {
+ _console->printObject(_gameObjectAddress);
error("initStackBaseWithSelector: error while registering the first selector in the call stack");
}
@@ -445,6 +595,7 @@ void SciEngine::runGame() {
_gamestate->_segMan->resetSegMan();
initGame();
initStackBaseWithSelector(SELECTOR(play));
+ patchGameSaveRestore(_gamestate->_segMan);
_gamestate->gameIsRestarting = GAMEISRESTARTING_RESTART;
if (_gfxMenu)
_gfxMenu->reset();
@@ -453,6 +604,7 @@ void SciEngine::runGame() {
_gamestate->abortScriptProcessing = kAbortNone;
_gamestate->_executionStack.clear();
initStackBaseWithSelector(SELECTOR(replay));
+ patchGameSaveRestore(_gamestate->_segMan);
_gamestate->shrinkStackToBase();
_gamestate->abortScriptProcessing = kAbortNone;
} else {
@@ -521,20 +673,6 @@ Common::String SciEngine::getSavegamePattern() const {
}
Common::String SciEngine::getFilePrefix() const {
- 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
- } 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";
- }
return _targetName;
}
@@ -549,6 +687,19 @@ Common::String SciEngine::unwrapFilename(const Common::String &name) const {
return name;
}
+int SciEngine::inQfGImportRoom() const {
+ if (_gameId == GID_QFG2 && _gamestate->currentRoomNumber() == 805) {
+ // QFG2 character import screen
+ return 2;
+ } else if (_gameId == GID_QFG3 && _gamestate->currentRoomNumber() == 54) {
+ // QFG3 character import screen
+ return 3;
+ } else if (_gameId == GID_QFG4 && _gamestate->currentRoomNumber() == 54) {
+ return 4;
+ }
+ return 0;
+}
+
void SciEngine::pauseEngineIntern(bool pause) {
_mixer->pauseAll(pause);
}
@@ -563,7 +714,7 @@ void SciEngine::syncSoundSettings() {
int soundVolumeMusic = (mute ? 0 : ConfMan.getInt("music_volume"));
if (_gamestate && g_sci->_soundCmd) {
- int vol = (soundVolumeMusic + 1) * SoundCommandParser::kMaxSciVolume / Audio::Mixer::kMaxMixerVolume;
+ int vol = (soundVolumeMusic + 1) * MUSIC_MASTERVOLUME_MAX / Audio::Mixer::kMaxMixerVolume;
g_sci->_soundCmd->setMasterVolume(vol);
}
}
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 72d6e7e0cb..7239abad17 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -53,6 +53,7 @@ class Console;
class AudioPlayer;
class SoundCommandParser;
class EventManager;
+class SegManager;
class GfxAnimate;
class GfxCache;
@@ -143,8 +144,9 @@ enum SciGameId {
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_MOTHERGOOSE, // this one is the SCI0 version
+ GID_MOTHERGOOSE256, // this one handles SCI1 and SCI1.1 variants, at least those 2 share a bit in common
+ GID_MOTHERGOOSEHIRES, // this one is the SCI2.1 hires version, completely different from the other ones
GID_MSASTROCHICKEN,
GID_PEPPER,
GID_PHANTASMAGORIA,
@@ -232,7 +234,8 @@ public:
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; }
+ inline reg_t getGameObject() const { return _gameObjectAddress; }
+ inline reg_t getGameSuperClassAddress() const { return _gameSuperClassAddress; }
Common::RandomSource &getRNG() { return _rng; }
@@ -247,11 +250,19 @@ public:
/** Remove the 'TARGET-' prefix of the given filename, if present. */
Common::String unwrapFilename(const Common::String &name) const;
+ /**
+ * Checks if we are in a QfG import screen, where special handling
+ * of file-listings is performed.
+ */
+ int inQfGImportRoom() const;
+
void sleep(uint32 msecs);
void scriptDebug();
bool checkExportBreakpoint(uint16 script, uint16 pubfunct);
- bool checkSelectorBreakpoint(reg_t send_obj, int selector);
+ bool checkSelectorBreakpoint(BreakpointType breakpointType, reg_t send_obj, int selector);
+
+ void patchGameSaveRestore(SegManager *segMan);
public:
@@ -341,7 +352,8 @@ private:
Vocabulary *_vocabulary;
int16 _vocabularyLanguage;
EventManager *_eventMan;
- reg_t _gameObj; /**< Pointer to the game object */
+ reg_t _gameObjectAddress; /**< Pointer to the game object */
+ reg_t _gameSuperClassAddress; // Address of the super class of the game object
Console *_console;
Common::RandomSource _rng;
};
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index 55c3640c9d..20bac4a2c0 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -73,6 +73,8 @@ public:
bool loadResource(const byte *data, uint size);
virtual uint32 property(int prop, uint32 param);
+ bool useRhythmChannel() const { return _rhythmKeyMap != NULL; }
+
private:
enum ChannelID {
kLeftChannel = 1,
@@ -171,12 +173,14 @@ public:
int open(ResourceManager *resMan);
void close();
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return MidiDriver_AdLib::kVoices; }
bool hasRhythmChannel() const { return false; }
void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
void playSwitch(bool play) { static_cast<MidiDriver_AdLib *>(_driver)->playSwitch(play); }
void loadInstrument(int idx, byte *data);
+
+ int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); }
};
static const byte registerOffset[MidiDriver_AdLib::kVoices] = {
@@ -586,7 +590,7 @@ void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) {
}
// Set patch if different from current patch
- if ((patch != _voices[voice].patch) && _playSwitch)
+ if (patch != _voices[voice].patch)
setPatch(voice, patch);
_voices[voice].velocity = velocity;
@@ -833,7 +837,7 @@ void MidiPlayer_AdLib::close() {
}
}
-byte MidiPlayer_AdLib::getPlayId() {
+byte MidiPlayer_AdLib::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
return 0x01;
diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp
index 4fb9146b53..4b591eb609 100644
--- a/engines/sci/sound/drivers/amigamac.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -966,7 +966,7 @@ bool MidiDriver_AmigaMac::loadInstrumentsSCI1(Common::SeekableReadStream &file)
class MidiPlayer_AmigaMac : public MidiPlayer {
public:
MidiPlayer_AmigaMac(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_AmigaMac(g_system->getMixer()); }
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return MidiDriver_AmigaMac::kVoices; }
bool hasRhythmChannel() const { return false; }
void setVolume(byte volume) { static_cast<MidiDriver_AmigaMac *>(_driver)->setVolume(volume); }
@@ -978,7 +978,7 @@ MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version) {
return new MidiPlayer_AmigaMac(version);
}
-byte MidiPlayer_AmigaMac::getPlayId() {
+byte MidiPlayer_AmigaMac::getPlayId() const {
if (_version > SCI_VERSION_0_LATE)
return 0x06;
diff --git a/engines/sci/sound/drivers/cms.cpp b/engines/sci/sound/drivers/cms.cpp
new file mode 100644
index 0000000000..cd7b101f03
--- /dev/null
+++ b/engines/sci/sound/drivers/cms.cpp
@@ -0,0 +1,817 @@
+/* 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/sound/drivers/mididriver.h"
+
+#include "sound/softsynth/emumidi.h"
+#include "sound/softsynth/cms.h"
+#include "sound/mixer.h"
+
+#include "sci/resource.h"
+
+namespace Sci {
+
+// FIXME: We don't seem to be sending the polyphony init data, so disable this for now
+#define CMS_DISABLE_VOICE_MAPPING
+
+class MidiDriver_CMS : public MidiDriver_Emulated {
+public:
+ MidiDriver_CMS(Audio::Mixer *mixer, ResourceManager *resMan)
+ : MidiDriver_Emulated(mixer), _resMan(resMan), _cms(0), _rate(0), _playSwitch(true), _masterVolume(0) {
+ }
+
+ int open();
+ void close();
+
+ void send(uint32 b);
+ uint32 property(int prop, uint32 param);
+
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ bool isStereo() const { return true; }
+ int getRate() const { return _rate; }
+
+ void playSwitch(bool play);
+private:
+ void generateSamples(int16 *buffer, int len);
+
+ ResourceManager *_resMan;
+ CMSEmulator *_cms;
+
+ void writeToChip1(int address, int data);
+ void writeToChip2(int address, int data);
+
+ int32 _samplesPerCallback;
+ int32 _samplesPerCallbackRemainder;
+ int32 _samplesTillCallback;
+ int32 _samplesTillCallbackRemainder;
+
+ int _rate;
+ bool _playSwitch;
+ uint16 _masterVolume;
+
+ uint8 *_patchData;
+
+ struct Channel {
+ Channel()
+ : patch(0), volume(0), pan(0x40), hold(0), extraVoices(0),
+ pitchWheel(0x2000), pitchModifier(0), pitchAdditive(false),
+ lastVoiceUsed(0) {
+ }
+
+ uint8 patch;
+ uint8 volume;
+ uint8 pan;
+ uint8 hold;
+ uint8 extraVoices;
+ uint16 pitchWheel;
+ uint8 pitchModifier;
+ bool pitchAdditive;
+ uint8 lastVoiceUsed;
+ };
+
+ Channel _channel[16];
+
+ struct Voice {
+ Voice() : channel(0xFF), note(0xFF), sustained(0xFF), ticks(0),
+ turnOffTicks(0), patchDataPtr(0), patchDataIndex(0),
+ amplitudeTimer(0), amplitudeModifier(0), turnOff(false),
+ velocity(0) {
+ }
+
+ uint8 channel;
+ uint8 note;
+ uint8 sustained;
+ uint16 ticks;
+ uint16 turnOffTicks;
+ const uint8 *patchDataPtr;
+ uint8 patchDataIndex;
+ uint8 amplitudeTimer;
+ uint8 amplitudeModifier;
+ bool turnOff;
+ uint8 velocity;
+ };
+
+ Voice _voice[12];
+
+ void voiceOn(int voice, int note, int velocity);
+ void voiceOff(int voice);
+
+ void noteSend(int voice);
+
+ void noteOn(int channel, int note, int velocity);
+ void noteOff(int channel, int note);
+ void controlChange(int channel, int control, int value);
+ void pitchWheel(int channel, int value);
+
+ void voiceMapping(int channel, int value);
+ void bindVoices(int channel, int voices);
+ void unbindVoices(int channel, int voices);
+ void donateVoices();
+ int findVoice(int channel);
+
+ int findVoiceBasic(int channel);
+
+ void updateVoiceAmplitude(int voice);
+ void setupVoiceAmplitude(int voice);
+
+ uint8 _octaveRegs[2][3];
+
+ static const int _timerFreq = 60;
+
+ static const int _frequencyTable[];
+ static const int _velocityTable[];
+};
+
+const int MidiDriver_CMS::_frequencyTable[] = {
+ 3, 10, 17, 24,
+ 31, 38, 46, 51,
+ 58, 64, 71, 77,
+ 83, 89, 95, 101,
+ 107, 113, 119, 124,
+ 130, 135, 141, 146,
+ 151, 156, 162, 167,
+ 172, 177, 182, 186,
+ 191, 196, 200, 205,
+ 209, 213, 217, 222,
+ 226, 230, 234, 238,
+ 242, 246, 250, 253
+};
+
+const int MidiDriver_CMS::_velocityTable[] = {
+ 1, 3, 6, 8, 9, 10, 11, 12,
+ 12, 13, 13, 14, 14, 14, 15, 15,
+ 0, 1, 2, 2, 3, 4, 4, 5,
+ 6, 6, 7, 8, 8, 9, 10, 10
+};
+
+int MidiDriver_CMS::open() {
+ if (_cms)
+ return MERR_ALREADY_OPEN;
+
+ assert(_resMan);
+ Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), 0);
+ if (!res)
+ return -1;
+
+ _patchData = new uint8[res->size];
+ memcpy(_patchData, res->data, res->size);
+
+ for (uint i = 0; i < ARRAYSIZE(_channel); ++i)
+ _channel[i] = Channel();
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i)
+ _voice[i] = Voice();
+
+ _rate = _mixer->getOutputRate();
+ _cms = new CMSEmulator(_rate);
+ assert(_cms);
+ _playSwitch = true;
+ _masterVolume = 0;
+
+ for (int i = 0; i < 31; ++i) {
+ writeToChip1(i, 0);
+ writeToChip2(i, 0);
+ }
+
+ writeToChip1(0x14, 0xFF);
+ writeToChip2(0x14, 0xFF);
+
+ writeToChip1(0x1C, 1);
+ writeToChip2(0x1C, 1);
+
+ _samplesPerCallback = getRate() / _timerFreq;
+ _samplesPerCallbackRemainder = getRate() % _timerFreq;
+ _samplesTillCallback = 0;
+ _samplesTillCallbackRemainder = 0;
+
+ int retVal = MidiDriver_Emulated::open();
+ if (retVal != 0)
+ return retVal;
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
+ return 0;
+}
+
+void MidiDriver_CMS::close() {
+ _mixer->stopHandle(_mixerSoundHandle);
+
+ delete[] _patchData;
+ delete _cms;
+ _cms = 0;
+}
+
+void MidiDriver_CMS::send(uint32 b) {
+ const uint8 command = b & 0xf0;
+ const uint8 channel = b & 0xf;
+ const uint8 op1 = (b >> 8) & 0xff;
+ const uint8 op2 = (b >> 16) & 0xff;
+
+ switch (command) {
+ case 0x80:
+ noteOff(channel, op1);
+ break;
+
+ case 0x90:
+ noteOn(channel, op1, op2);
+ break;
+
+ case 0xB0:
+ controlChange(channel, op1, op2);
+ break;
+
+ case 0xC0:
+ _channel[channel].patch = op1;
+ break;
+
+ case 0xE0:
+ pitchWheel(channel, (op1 & 0x7f) | ((op2 & 0x7f) << 7));
+ break;
+
+ default:
+ break;
+ }
+}
+
+uint32 MidiDriver_CMS::property(int prop, uint32 param) {
+ switch (prop) {
+ case MIDI_PROP_MASTER_VOLUME:
+ if (param != 0xffff)
+ _masterVolume = param;
+ return _masterVolume;
+
+ default:
+ return MidiDriver_Emulated::property(prop, param);
+ }
+}
+
+void MidiDriver_CMS::playSwitch(bool play) {
+ _playSwitch = play;
+}
+
+void MidiDriver_CMS::writeToChip1(int address, int data) {
+ _cms->portWrite(0x221, address);
+ _cms->portWrite(0x220, data);
+
+ if (address >= 16 && address <= 18)
+ _octaveRegs[0][address - 16] = data;
+}
+
+void MidiDriver_CMS::writeToChip2(int address, int data) {
+ _cms->portWrite(0x223, address);
+ _cms->portWrite(0x222, data);
+
+ if (address >= 16 && address <= 18)
+ _octaveRegs[1][address - 16] = data;
+}
+
+void MidiDriver_CMS::voiceOn(int voiceNr, int note, int velocity) {
+ Voice &voice = _voice[voiceNr];
+ voice.note = note;
+ voice.turnOff = false;
+ voice.patchDataIndex = 0;
+ voice.amplitudeTimer = 0;
+ voice.ticks = 0;
+ voice.turnOffTicks = 0;
+ voice.patchDataPtr = _patchData + READ_LE_UINT16(&_patchData[_channel[voice.channel].patch * 2]);
+ if (velocity)
+ velocity = _velocityTable[(velocity >> 3)];
+ voice.velocity = velocity;
+ noteSend(voiceNr);
+}
+
+void MidiDriver_CMS::voiceOff(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
+ voice.velocity = 0;
+ voice.note = 0xFF;
+ voice.sustained = 0;
+ voice.turnOff = false;
+ voice.patchDataIndex = 0;
+ voice.amplitudeTimer = 0;
+ voice.amplitudeModifier = 0;
+ voice.ticks = 0;
+ voice.turnOffTicks = 0;
+
+ setupVoiceAmplitude(voiceNr);
+}
+
+void MidiDriver_CMS::noteSend(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
+
+ int frequency = (CLIP<int>(voice.note, 21, 116) - 21) * 4;
+ if (_channel[voice.channel].pitchModifier) {
+ int modifier = _channel[voice.channel].pitchModifier;
+
+ if (!_channel[voice.channel].pitchAdditive) {
+ if (frequency > modifier)
+ frequency -= modifier;
+ else
+ frequency = 0;
+ } else {
+ int tempFrequency = 384 - frequency;
+ if (modifier < tempFrequency)
+ frequency += modifier;
+ else
+ frequency = 383;
+ }
+ }
+
+ int chipNumber = 0;
+ if (voiceNr >= 6) {
+ voiceNr -= 6;
+ chipNumber = 1;
+ }
+
+ int octave = 0;
+ while (frequency >= 48) {
+ frequency -= 48;
+ ++octave;
+ }
+
+ frequency = _frequencyTable[frequency];
+
+ if (chipNumber == 1)
+ writeToChip2(8 + voiceNr, frequency);
+ else
+ writeToChip1(8 + voiceNr, frequency);
+
+ uint8 octaveData = _octaveRegs[chipNumber][voiceNr >> 1];
+
+ if (voiceNr & 1) {
+ octaveData &= 0x0F;
+ octaveData |= (octave << 4);
+ } else {
+ octaveData &= 0xF0;
+ octaveData |= octave;
+ }
+
+ if (chipNumber == 1)
+ writeToChip2(0x10 + (voiceNr >> 1), octaveData);
+ else
+ writeToChip1(0x10 + (voiceNr >> 1), octaveData);
+}
+
+void MidiDriver_CMS::noteOn(int channel, int note, int velocity) {
+ if (note < 21 || note > 116)
+ return;
+
+ if (velocity == 0) {
+ noteOff(channel, note);
+ return;
+ }
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channel && _voice[i].note == note) {
+ _voice[i].sustained = 0;
+ voiceOff(i);
+ voiceOn(i, note, velocity);
+ return;
+ }
+ }
+
+#ifdef CMS_DISABLE_VOICE_MAPPING
+ int voice = findVoiceBasic(channel);
+#else
+ int voice = findVoice(channel);
+#endif
+ if (voice != -1)
+ voiceOn(voice, note, velocity);
+}
+
+int MidiDriver_CMS::findVoiceBasic(int channel) {
+ int voice = -1;
+ int oldestVoice = -1;
+ int oldestAge = -1;
+
+ // Try to find a voice assigned to this channel that is free (round-robin)
+ for (int i = 0; i < ARRAYSIZE(_voice); i++) {
+ int v = (_channel[channel].lastVoiceUsed + i + 1) % ARRAYSIZE(_voice);
+
+ if (_voice[v].note == 0xFF) {
+ voice = v;
+ break;
+ }
+
+ // We also keep track of the oldest note in case the search fails
+ if (_voice[v].ticks > oldestAge) {
+ oldestAge = _voice[v].ticks;
+ oldestVoice = v;
+ }
+ }
+
+ if (voice == -1) {
+ if (oldestVoice != -1) {
+ voiceOff(oldestVoice);
+ voice = oldestVoice;
+ } else {
+ return -1;
+ }
+ }
+
+ _voice[voice].channel = channel;
+ _channel[channel].lastVoiceUsed = voice;
+ return voice;
+}
+
+void MidiDriver_CMS::noteOff(int channel, int note) {
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channel && _voice[i].note == note) {
+ if (_channel[channel].hold != 0)
+ _voice[i].sustained = true;
+ else
+ _voice[i].turnOff = true;
+ }
+ }
+}
+
+void MidiDriver_CMS::controlChange(int channel, int control, int value) {
+ switch (control) {
+ case 7:
+ if (value) {
+ value >>= 3;
+ if (!value)
+ ++value;
+ }
+
+ _channel[channel].volume = value;
+ break;
+
+ case 10:
+ _channel[channel].pan = value;
+ break;
+
+ case 64:
+ _channel[channel].hold = value;
+
+ if (!value) {
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channel && _voice[i].sustained) {
+ _voice[i].sustained = 0;
+ _voice[i].turnOff = true;
+ }
+ }
+ }
+ break;
+
+ case 75:
+#ifndef CMS_DISABLE_VOICE_MAPPING
+ voiceMapping(channel, value);
+#endif
+ break;
+
+ case 123:
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channel && _voice[i].note != 0xFF)
+ voiceOff(i);
+ }
+ break;
+
+ default:
+ return;
+ }
+}
+
+void MidiDriver_CMS::pitchWheel(int channelNr, int value) {
+ Channel &channel = _channel[channelNr];
+ channel.pitchWheel = value;
+ channel.pitchAdditive = false;
+ channel.pitchModifier = 0;
+
+ if (value < 0x2000) {
+ channel.pitchModifier = (0x2000 - value) / 170;
+ } else if (value > 0x2000) {
+ channel.pitchModifier = (value - 0x2000) / 170;
+ channel.pitchAdditive = true;
+ }
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channelNr && _voice[i].note != 0xFF)
+ noteSend(i);
+ }
+}
+
+void MidiDriver_CMS::voiceMapping(int channelNr, int value) {
+ int curVoices = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channelNr)
+ ++curVoices;
+ }
+
+ curVoices += _channel[channelNr].extraVoices;
+
+ if (curVoices == value) {
+ return;
+ } else if (curVoices < value) {
+ bindVoices(channelNr, value - curVoices);
+ } else {
+ unbindVoices(channelNr, curVoices - value);
+ donateVoices();
+ }
+}
+
+void MidiDriver_CMS::bindVoices(int channel, int voices) {
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == 0xFF)
+ continue;
+
+ Voice &voice = _voice[i];
+ voice.channel = channel;
+
+ if (voice.note != 0xFF)
+ voiceOff(i);
+
+ --voices;
+ if (voices == 0)
+ break;
+ }
+
+ _channel[channel].extraVoices += voices;
+
+ // The original called "PatchChange" here, since this just
+ // copies the value of _channel[channel].patch to itself
+ // it is left out here though.
+}
+
+void MidiDriver_CMS::unbindVoices(int channelNr, int voices) {
+ Channel &channel = _channel[channelNr];
+
+ if (channel.extraVoices >= voices) {
+ channel.extraVoices -= voices;
+ } else {
+ voices -= channel.extraVoices;
+ channel.extraVoices = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == channelNr
+ && _voice[i].note == 0xFF) {
+ --voices;
+ if (voices == 0)
+ return;
+ }
+ }
+
+ do {
+ uint16 voiceTime = 0;
+ uint voiceNr = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel != channelNr)
+ continue;
+
+ uint16 curTime = _voice[i].turnOffTicks;
+ if (curTime)
+ curTime += 0x8000;
+ else
+ curTime = _voice[i].ticks;
+
+ if (curTime >= voiceTime) {
+ voiceNr = i;
+ voiceTime = curTime;
+ }
+ }
+
+ _voice[voiceNr].sustained = 0;
+ voiceOff(voiceNr);
+ _voice[voiceNr].channel = 0xFF;
+ --voices;
+ } while (voices != 0);
+ }
+}
+
+void MidiDriver_CMS::donateVoices() {
+ int freeVoices = 0;
+
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].channel == 0xFF)
+ ++freeVoices;
+ }
+
+ if (!freeVoices)
+ return;
+
+ for (uint i = 0; i < ARRAYSIZE(_channel); ++i) {
+ Channel &channel = _channel[i];
+
+ if (!channel.extraVoices) {
+ continue;
+ } else if (channel.extraVoices < freeVoices) {
+ freeVoices -= channel.extraVoices;
+ channel.extraVoices = 0;
+ bindVoices(i, channel.extraVoices);
+ } else {
+ channel.extraVoices -= freeVoices;
+ bindVoices(i, freeVoices);
+ return;
+ }
+ }
+}
+
+int MidiDriver_CMS::findVoice(int channelNr) {
+ Channel &channel = _channel[channelNr];
+ int voiceNr = channel.lastVoiceUsed;
+
+ int newVoice = 0;
+ uint16 newVoiceTime = 0;
+
+ bool loopDone = false;
+ do {
+ ++voiceNr;
+
+ if (voiceNr == 12)
+ voiceNr = 0;
+
+ Voice &voice = _voice[voiceNr];
+
+ if (voiceNr == channel.lastVoiceUsed)
+ loopDone = true;
+
+ if (voice.channel == channelNr) {
+ if (voice.note == 0xFF) {
+ channel.lastVoiceUsed = voiceNr;
+ return voiceNr;
+ }
+
+ uint16 curTime = voice.turnOffTicks;
+ if (curTime)
+ curTime += 0x8000;
+ else
+ curTime = voice.ticks;
+
+ if (curTime >= newVoiceTime) {
+ newVoice = voiceNr;
+ newVoiceTime = curTime;
+ }
+ }
+ } while (!loopDone);
+
+ if (newVoiceTime > 0) {
+ voiceNr = newVoice;
+ _voice[voiceNr].sustained = 0;
+ voiceOff(voiceNr);
+ channel.lastVoiceUsed = voiceNr;
+ return voiceNr;
+ } else {
+ return -1;
+ }
+}
+
+void MidiDriver_CMS::updateVoiceAmplitude(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
+
+ if (voice.amplitudeTimer != 0 && voice.amplitudeTimer != 254) {
+ --voice.amplitudeTimer;
+ return;
+ } else if (voice.amplitudeTimer == 254) {
+ if (!voice.turnOff)
+ return;
+
+ voice.amplitudeTimer = 0;
+ }
+
+ int nextDataIndex = voice.patchDataIndex;
+ uint8 timerData = 0;
+ uint8 amplitudeData = voice.patchDataPtr[nextDataIndex];
+
+ if (amplitudeData == 255) {
+ timerData = amplitudeData = 0;
+ voiceOff(voiceNr);
+ } else {
+ timerData = voice.patchDataPtr[nextDataIndex + 1];
+ nextDataIndex += 2;
+ }
+
+ voice.patchDataIndex = nextDataIndex;
+ voice.amplitudeTimer = timerData;
+ voice.amplitudeModifier = amplitudeData;
+}
+
+void MidiDriver_CMS::setupVoiceAmplitude(int voiceNr) {
+ Voice &voice = _voice[voiceNr];
+ uint amplitude = 0;
+
+ if (_channel[voice.channel].volume && voice.velocity
+ && voice.amplitudeModifier && _masterVolume) {
+ amplitude = _channel[voice.channel].volume * voice.velocity;
+ amplitude /= 0x0F;
+ amplitude *= voice.amplitudeModifier;
+ amplitude /= 0x0F;
+ amplitude *= _masterVolume;
+ amplitude /= 0x0F;
+
+ if (!amplitude)
+ ++amplitude;
+ }
+
+ uint8 amplitudeData = 0;
+ int pan = _channel[voice.channel].pan >> 2;
+ if (pan >= 16) {
+ amplitudeData = (amplitude * (31 - pan) / 0x0F) & 0x0F;
+ amplitudeData |= (amplitude << 4);
+ } else {
+ amplitudeData = (amplitude * pan / 0x0F) & 0x0F;
+ amplitudeData <<= 4;
+ amplitudeData |= amplitude;
+ }
+
+ if (!_playSwitch)
+ amplitudeData = 0;
+
+ if (voiceNr >= 6)
+ writeToChip2(voiceNr - 6, amplitudeData);
+ else
+ writeToChip1(voiceNr, amplitudeData);
+}
+
+void MidiDriver_CMS::generateSamples(int16 *buffer, int len) {
+ while (len) {
+ if (!_samplesTillCallback) {
+ for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
+ if (_voice[i].note == 0xFF)
+ continue;
+
+ ++_voice[i].ticks;
+ if (_voice[i].turnOff)
+ ++_voice[i].turnOffTicks;
+
+ updateVoiceAmplitude(i);
+ setupVoiceAmplitude(i);
+ }
+
+ _samplesTillCallback = _samplesPerCallback;
+ _samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
+ if (_samplesTillCallbackRemainder >= _timerFreq) {
+ _samplesTillCallback++;
+ _samplesTillCallbackRemainder -= _timerFreq;
+ }
+ }
+
+ int32 render = MIN<int32>(len, _samplesTillCallback);
+ len -= render;
+ _samplesTillCallback -= render;
+ _cms->readBuffer(buffer, render);
+ buffer += render * 2;
+ }
+}
+
+
+class MidiPlayer_CMS : public MidiPlayer {
+public:
+ MidiPlayer_CMS(SciVersion version) : MidiPlayer(version) {
+ }
+
+ int open(ResourceManager *resMan) {
+ if (_driver)
+ return MERR_ALREADY_OPEN;
+
+ _driver = new MidiDriver_CMS(g_system->getMixer(), resMan);
+ int driverRetVal = _driver->open();
+ if (driverRetVal != 0)
+ return driverRetVal;
+
+ return 0;
+ }
+
+ void close() {
+ _driver->setTimerCallback(0, 0);
+ _driver->close();
+ delete _driver;
+ _driver = 0;
+ }
+
+ bool hasRhythmChannel() const { return false; }
+ byte getPlayId() const { return 9; }
+ int getPolyphony() const { return 12; }
+
+ void playSwitch(bool play) { static_cast<MidiDriver_CMS *>(_driver)->playSwitch(play); }
+};
+
+MidiPlayer *MidiPlayer_CMS_create(SciVersion version) {
+ return new MidiPlayer_CMS(version);
+}
+
+} // End of namespace SCI
+
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index ab9b2e3df5..7560c62f4f 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -59,7 +59,7 @@ public:
void send(uint32 b);
void sysEx(const byte *msg, uint16 length);
bool hasRhythmChannel() const { return false; }
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return kVoices; } // 9 in SCI1?
void setVolume(byte volume);
int getVolume();
@@ -633,7 +633,7 @@ void MidiPlayer_Fb01::sysEx(const byte *msg, uint16 length) {
g_system->updateScreen();
}
-byte MidiPlayer_Fb01::getPlayId() {
+byte MidiPlayer_Fb01::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
return 0x01;
diff --git a/engines/sci/sound/drivers/map-mt32-to-gm.h b/engines/sci/sound/drivers/map-mt32-to-gm.h
index a552ef0608..05d1aeba24 100644
--- a/engines/sci/sound/drivers/map-mt32-to-gm.h
+++ b/engines/sci/sound/drivers/map-mt32-to-gm.h
@@ -421,131 +421,131 @@ static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = {
{"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (PQ2) */
{"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */
{"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */
- {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (CB) */
+ {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (LB1) */
{"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */
{"BagPipes ", 109, MIDI_UNMAPPED},
- {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (HQ) */
+ {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (QFG1) */
{"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */
- {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (iceMan) */
- {"Bells MS", 112, MIDI_UNMAPPED}, /* + (HQ) */
- {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (CB) */
+ {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (Iceman) */
+ {"Bells MS", 112, MIDI_UNMAPPED}, /* + (QFG1) */
+ {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (LB1) */
{"Bird Tweet", 123, MIDI_UNMAPPED},
- {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (iceMan) */
+ {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (Iceman) */
{"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (HBoG) */
- {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (HQ) */
- {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (CoC) */
- {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (iceMan) */
+ {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 69}, /* R (Hoyle) */
+ {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (QFG1) */
+ {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (Camelot) */
+ {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (Iceman) */
{"Chop ", 117, MIDI_UNMAPPED},
- {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (CoC) */
+ {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (Camelot) */
{"ClarinetMS", 71, MIDI_UNMAPPED},
{"Claves ", MIDI_MAPPED_TO_RHYTHM, 75}, /* R (PQ2) */
- {"Claw MS", 118, MIDI_UNMAPPED}, /* + (HQ) */
- {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (CB) */
+ {"Claw MS", 118, MIDI_UNMAPPED}, /* + (QFG1) */
+ {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (LB1) */
{"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (KQ1) */
- {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, /* R (HQ) */
+ {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 64}, /* R (QFG1) */
{"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */
- {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */
+ {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
{"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
- {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (CB) */
- {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, /* R +++ (iceMan) */
- {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */
- {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (CoC, HQ) */
+ {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (LB1) */
+ {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 57}, /* R +++ (Iceman) */
+ {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 55}, /* R ? (Camelot, QFG1) */
{"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 57}, /* R ? (KQ1) */
{"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */
- {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HBoG) */
- {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (iceMan) */
- {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (iceMan) */
+ {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Hoyle) */
+ {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (Iceman) */
+ {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (Iceman) */
{"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */
{"ElecBassMS", 33, MIDI_UNMAPPED},
- {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (iceMan) */
+ {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (Iceman) */
{"EnglHornMS", 69, MIDI_UNMAPPED},
{"FantasiaKA", 88, MIDI_UNMAPPED},
{"Fantasy ", 99, MIDI_UNMAPPED}, /* + (PQ2) */
- {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (CoC, HQ) */
- {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (iceMan) */
- {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (iceMan) */
- {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */
- {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */
- {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (HQ) */
+ {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (Camelot, QFG1) */
+ {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (Iceman) */
+ {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (Iceman) */
+ {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (QFG1) */
{"FogHorn MS", 58, MIDI_UNMAPPED},
- {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (HQ) */
- {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (CB) */
+ {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (QFG1) */
+ {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (LB1) */
{"GameSnd MS", 80, MIDI_UNMAPPED},
- {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (HQ) */
- {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */
- {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (HBoG) */
- {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (iceMan) */
- {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */
- {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */
- {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (iceMan) */
+ {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (QFG1) */
+ {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (Hoyle) */
+ {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (Iceman) */
+ {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
+ {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (Camelot, QFG1) */
+ {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (Iceman) */
{"KNIFE ", 120, MIDI_UNMAPPED}, /* ? (LSL3) */
- {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (HQ) */
+ {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (QFG1) */
{"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */
{"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */
- {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (HQ) */
- {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (HQ) */
- {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (iceMan) */
- {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (iceMan) */
+ {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (QFG1) */
+ {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (Iceman) */
+ {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (Iceman) */
{"OCEANSOUND", 122, MIDI_UNMAPPED}, /* + (LSL3) */
{"Oboe 2001 ", 68, MIDI_UNMAPPED}, /* + (PQ2) */
- {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (iceMan) */
- {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (iceMan) */
- {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */
- {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (iceMan) */
+ {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (Iceman) */
+ {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (Iceman) */
+ {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */
+ {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 40}, /* R ? (Iceman) */
{"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */
{"PinkBassMS", 39, MIDI_UNMAPPED},
- {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (CB) */
+ {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (LB1) */
{"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
- {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (HQ) */
- {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (CB, CoC) */
- {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (CB) */
- {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (CoC) */
- {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (CB) */
- {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Coc) */
+ {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (QFG1) */
+ {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (LauraBow1, Camelot) */
+ {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (Camelot) */
+ {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (LB1) */
+ {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Camelot) */
{"RevCymb MS", 119, MIDI_UNMAPPED},
- {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (CB) */
+ {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (LB1) */
{"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 37}, /* R */
{"SHOWER ", 52, MIDI_UNMAPPED}, /* ? (LSL3) */
{"SQ Bass MS", 32, MIDI_UNMAPPED}, /* + (SQ3) */
- {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (iceMan) */
- {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (iceMan) */
- {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (HQ) */
- {"Some Birds", 123, MIDI_UNMAPPED}, /* + (CB) */
- {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (iceMan) */
- {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (CoC) */
+ {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (Iceman) */
+ {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (Iceman) */
+ {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 38}, /* R (QFG1) */
+ {"Some Birds", 123, MIDI_UNMAPPED}, /* + (LB1) */
+ {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (Iceman) */
+ {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (Camelot) */
{"SqurWaveMS", 80, MIDI_UNMAPPED},
- {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (iceMan) */
- {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (iceMan) */
- {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (HQ) */
- {"String MS", 45, MIDI_UNMAPPED}, /* + (CoC) */
+ {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (Iceman) */
+ {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (Iceman) */
+ {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (QFG1) */
+ {"String MS", 45, MIDI_UNMAPPED}, /* + (Camelot) */
{"Syn-Choir ", 91, MIDI_UNMAPPED},
{"Syn Brass4", 63, MIDI_UNMAPPED}, /* ++ (PQ2) */
{"SynBass MS", 38, MIDI_UNMAPPED},
- {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (CB,HQ) */
- {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (HQ) */
- {"Taiko ", 116, 35}, /* +++ (Coc) */
+ {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (LB1, QFG1) */
+ {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (QFG1) */
+ {"Taiko ", 116, 35}, /* +++ (Camelot) */
{"Taiko Rim ", 118, 37}, /* +++ (LSL3) */
- {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (CB) */
- {"Tom MS", 117, 48}, /* +++ (iceMan) */
- {"Toms MS", 117, 48}, /* +++ (CoC, HQ) */
+ {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (LB1) */
+ {"Tom MS", 117, 48}, /* +++ (Iceman) */
+ {"Toms MS", 117, 48}, /* +++ (Camelot, QFG1) */
{"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */
- {"TriangleMS", 112, 81}, /* R (CoC) */
- {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (CoC) */
- {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, /* + (iceMan) */
+ {"TriangleMS", 112, 81}, /* R (Camelot) */
+ {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (Camelot) */
+ {"Type MS", MIDI_MAPPED_TO_RHYTHM, 39}, /* + (Iceman) */
{"WaterBells", 98, MIDI_UNMAPPED}, /* + (PQ2) */
{"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */
- {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (CB) */
- {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */
- {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ, iceMan) */
- {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC) */
- {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (CB) */
- {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC, HQ, iceMan) */
+ {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (LB1) */
+ {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (LB1) */
+ {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (QFG1, Iceman) */
+ {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot) */
+ {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (LB1) */
+ {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (Camelot, QFG1, Iceman) */
{0, 0, 0}
};
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 1ef0781906..8ba7a6a352 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -53,9 +53,10 @@ public:
void send(uint32 b);
void sysEx(const byte *msg, uint16 length);
bool hasRhythmChannel() const { return true; }
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return kVoices; }
- int getFirstChannel();
+ int getFirstChannel() const;
+ int getLastChannel() const;
void setVolume(byte volume);
int getVolume();
void setReverb(byte reverb);
@@ -97,7 +98,7 @@ private:
};
bool _isMt32;
- bool _isOldPatchFormat;
+ bool _useMT32Track;
bool _hasReverb;
bool _playSwitch;
int _masterVolume;
@@ -119,7 +120,7 @@ private:
byte _sysExBuf[kMaxSysExSize];
};
-MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
+MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _useMT32Track(true) {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
_driver = createMidi(dev);
@@ -139,6 +140,10 @@ MidiPlayer_Midi::~MidiPlayer_Midi() {
void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
uint8 patch = _channels[channel].mappedPatch;
+ assert(channel <= 15);
+ assert(note <= 127);
+ assert(velocity <= 127);
+
if (channel == MIDI_RHYTHM_CHANNEL) {
if (_percussionMap[note] == MIDI_UNMAPPED) {
debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note);
@@ -175,6 +180,7 @@ void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
// We assume that velocity 0 maps to 0 (for note off)
int mapIndex = _channels[channel].velocityMapIdx;
+ assert(velocity <= 127);
velocity = _velocityMap[mapIndex][velocity];
}
@@ -183,6 +189,8 @@ void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) {
}
void MidiPlayer_Midi::controlChange(int channel, int control, int value) {
+ assert(channel <= 15);
+
switch (control) {
case 0x07:
_channels[channel].volume = value;
@@ -232,6 +240,8 @@ void MidiPlayer_Midi::controlChange(int channel, int control, int value) {
void MidiPlayer_Midi::setPatch(int channel, int patch) {
bool resetVol = false;
+ assert(channel <= 15);
+
if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch))
return;
@@ -319,12 +329,19 @@ 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() {
+// NOTE: SSCI uses channels 1 through 8 for General MIDI as well, in the drivers I checked
+int MidiPlayer_Midi::getFirstChannel() const {
if (_isMt32)
return 1;
return 0;
}
+int MidiPlayer_Midi::getLastChannel() const {
+ if (_isMt32)
+ return 8;
+ return 15;
+}
+
void MidiPlayer_Midi::setVolume(byte volume) {
_masterVolume = volume;
@@ -772,6 +789,9 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
_percussionMap[i] = i;
_patchMap[i] = i;
_velocityMap[0][i] = i;
+ _velocityMap[1][i] = i;
+ _velocityMap[2][i] = i;
+ _velocityMap[3][i] = i;
_keyShift[i] = 0;
_volAdjust[i] = 0;
_velocityMapIdx[i] = 0;
@@ -808,17 +828,35 @@ int MidiPlayer_Midi::open(ResourceManager *resMan) {
// Detect the format of patch 1, so that we know what play mask to use
res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
if (!res)
- _isOldPatchFormat = false;
+ _useMT32Track = false;
else
- _isOldPatchFormat = !isMt32GmPatch(res->data, res->size);
+ _useMT32Track = !isMt32GmPatch(res->data, res->size);
+
+ // Check if the songs themselves have a GM track
+ if (!_useMT32Track) {
+ if (!resMan->isGMTrackIncluded())
+ _useMT32Track = true;
+ }
} else {
// No GM patch found, map instruments using MT-32 patch
warning("Game has no native support for General MIDI, applying auto-mapping");
+ // TODO: The MT-32 <-> GM mapping hasn't been worked on for SCI1 games. Throw
+ // a warning to the user
+ if (getSciVersion() >= SCI_VERSION_1_EGA)
+ warning("The automatic mapping for General MIDI hasn't been worked on for "
+ "SCI1 games. Music might sound wrong or broken. Please choose another "
+ "music driver for this game (e.g. Adlib or MT-32) if you are "
+ "experiencing issues with music");
+
// Modify velocity map to make low velocity notes a little louder
- for (uint i = 1; i < 0x40; i++)
+ for (uint i = 1; i < 0x40; i++) {
_velocityMap[0][i] = 0x20 + (i - 1) / 2;
+ _velocityMap[1][i] = 0x20 + (i - 1) / 2;
+ _velocityMap[2][i] = 0x20 + (i - 1) / 2;
+ _velocityMap[3][i] = 0x20 + (i - 1) / 2;
+ }
res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0);
@@ -872,7 +910,7 @@ void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) {
g_system->updateScreen();
}
-byte MidiPlayer_Midi::getPlayId() {
+byte MidiPlayer_Midi::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
case SCI_VERSION_0_LATE:
@@ -881,7 +919,7 @@ byte MidiPlayer_Midi::getPlayId() {
if (_isMt32)
return 0x0c;
else
- return _isOldPatchFormat ? 0x0c : 0x07;
+ return _useMT32Track ? 0x0c : 0x07;
}
}
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index 2db6f25c70..129159ecdc 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -32,6 +32,20 @@
namespace Sci {
+// Music patches in SCI games:
+// ===========================
+// 1.pat - MT-32 driver music patch
+// 2.pat - Yamaha FB01 driver music patch
+// 3.pat - Adlib driver music patch
+// 4.pat - Casio MT-540 (in earlier SCI0 games)
+// 4.pat - GM driver music patch (in later games that support GM)
+// 7.pat (newer) / patch.200 (older) - Mac driver music patch / Casio CSM-1
+// 9.pat (newer) / patch.005 (older) - Amiga driver music patch
+// 98.pat - Unknown, found in later SCI1.1 games. A MIDI format patch
+// 101.pat - CMS/PCjr driver music patch.
+// Only later PCjr drivers use this patch, earlier ones don't use a patch
+// bank.001 - older SCI0 Amiga instruments
+
class ResourceManager;
enum {
@@ -39,7 +53,6 @@ enum {
MIDI_PROP_MASTER_VOLUME = 0
};
-
#define MIDI_RHYTHM_CHANNEL 9
/* Special SCI sound stuff */
@@ -69,7 +82,7 @@ protected:
byte _reverb;
public:
- MidiPlayer(SciVersion version) : _reverb(0), _version(version) { }
+ MidiPlayer(SciVersion version) : _driver(0), _reverb(0), _version(version) { }
int open() {
ResourceManager *resMan = g_sci->getResMan(); // HACK
@@ -84,9 +97,10 @@ public:
MidiChannel *getPercussionChannel() { return _driver->getPercussionChannel(); }
virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); }
- virtual byte getPlayId() = 0;
+ virtual byte getPlayId() const = 0;
virtual int getPolyphony() const = 0;
- virtual int getFirstChannel() { return 0; }
+ virtual int getFirstChannel() const { return 0; }
+ virtual int getLastChannel() const { return 15; }
virtual void setVolume(byte volume) {
if(_driver)
@@ -97,7 +111,7 @@ public:
return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;
}
- virtual byte getReverb() { return _reverb; }
+ virtual byte getReverb() const { return _reverb; }
virtual void setReverb(byte reverb) { _reverb = reverb; }
virtual void playSwitch(bool play) {
@@ -116,6 +130,7 @@ extern MidiPlayer *MidiPlayer_AdLib_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_CMS_create(SciVersion version);
extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version);
extern MidiPlayer *MidiPlayer_Fb01_create(SciVersion version);
diff --git a/engines/sci/sound/drivers/pcjr.cpp b/engines/sci/sound/drivers/pcjr.cpp
index bdf90eff5c..93de072865 100644
--- a/engines/sci/sound/drivers/pcjr.cpp
+++ b/engines/sci/sound/drivers/pcjr.cpp
@@ -234,13 +234,13 @@ class MidiPlayer_PCJr : public MidiPlayer {
public:
MidiPlayer_PCJr(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_PCJr(g_system->getMixer()); }
int open(ResourceManager *resMan) { return static_cast<MidiDriver_PCJr *>(_driver)->open(getPolyphony()); }
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return 3; }
bool hasRhythmChannel() const { return false; }
void setVolume(byte volume) { static_cast<MidiDriver_PCJr *>(_driver)->_global_volume = volume; }
};
-byte MidiPlayer_PCJr::getPlayId() {
+byte MidiPlayer_PCJr::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
return 0x02;
@@ -259,11 +259,11 @@ class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr {
public:
MidiPlayer_PCSpeaker(SciVersion version) : MidiPlayer_PCJr(version) { }
- byte getPlayId();
+ byte getPlayId() const;
int getPolyphony() const { return 1; }
};
-byte MidiPlayer_PCSpeaker::getPlayId() {
+byte MidiPlayer_PCSpeaker::getPlayId() const {
switch (_version) {
case SCI_VERSION_0_EARLY:
return 0x04;
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 769df73365..d53f919f8f 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -53,6 +53,7 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) :
_ppqn = 1;
setTempo(16667);
+ _masterVolume = 15;
_volume = 127;
_signalSet = false;
@@ -418,7 +419,7 @@ void MidiParser_SCI::sendToDriver(uint32 midi) {
int channelVolume = (midi >> 16) & 0xFF;
// Remember, if we need to set it ourselves
_channelVolume[midiChannel] = channelVolume;
- // Adjust volume accordingly to current "global" volume
+ // Adjust volume accordingly to current local volume
channelVolume = channelVolume * _volume / 127;
midi = (midi & 0xFFF0) | ((channelVolume & 0xFF) << 16);
}
@@ -445,12 +446,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
}
if (_signalSet) {
_signalSet = false;
- if (!_pSnd->signal) {
- _pSnd->signal = _signalToSet;
- } else {
- // signal already set and waiting for getting to scripts, queue new one
- _pSnd->signalQueue.push_back(_signalToSet);
- }
+ _pSnd->setSignal(_signalToSet);
+
debugC(4, kDebugLevelSound, "signal %04x", _signalToSet);
}
@@ -613,12 +610,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
jumpToTick(_loopTick);
} else {
_pSnd->status = kSoundStopped;
- if (!_pSnd->signal) {
- _pSnd->signal = SIGNAL_OFFSET;
- } else {
- // signal already set and waiting for getting to scripts, queue new one
- _pSnd->signalQueue.push_back(SIGNAL_OFFSET);
- }
+ _pSnd->setSignal(SIGNAL_OFFSET);
debugC(4, kDebugLevelSound, "signal EOT");
}
@@ -668,6 +660,28 @@ void MidiParser_SCI::allNotesOff() {
memset(_active_notes, 0, sizeof(_active_notes));
}
+void MidiParser_SCI::setMasterVolume(byte masterVolume) {
+ assert(masterVolume <= MUSIC_MASTERVOLUME_MAX);
+ _masterVolume = masterVolume;
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE:
+ // update driver master volume
+ setVolume(_volume);
+ break;
+
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ case SCI_VERSION_2_1:
+ // directly set master volume (global volume is merged with channel volumes)
+ ((MidiPlayer *)_driver)->setVolume(masterVolume);
+ break;
+
+ default:
+ error("MidiParser_SCI::setVolume: Unsupported soundVersion");
+ }
+}
+
void MidiParser_SCI::setVolume(byte volume) {
assert(volume <= MUSIC_VOLUME_MAX);
_volume = volume;
@@ -676,8 +690,7 @@ void MidiParser_SCI::setVolume(byte volume) {
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;
+ int16 globalVolume = _volume * _masterVolume / MUSIC_VOLUME_MAX;
((MidiPlayer *)_driver)->setVolume(globalVolume);
break;
}
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index 90db06e539..9d0cb15e74 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -65,6 +65,7 @@ public:
}
void sendInitCommands();
void unloadMusic();
+ void setMasterVolume(byte masterVolume);
void setVolume(byte volume);
void stop() {
_abort_parse = true;
@@ -104,7 +105,8 @@ protected:
SoundResource::Track *_track;
MusicEntry *_pSnd;
uint32 _loopTick;
- byte _volume;
+ byte _masterVolume; // the overall master volume (same for all tracks)
+ byte _volume; // the global volume of the current track
bool _signalSet;
int16 _signalToSet;
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index fc1e56fcea..0dfa02c83f 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -30,6 +30,7 @@
#include "sci/sci.h"
#include "sci/console.h"
#include "sci/resource.h"
+#include "sci/engine/features.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/sound/midiparser_sci.h"
@@ -65,9 +66,20 @@ void SciMusic::init() {
// 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));
- switch (MidiDriver::getMusicType(dev)) {
+ uint32 deviceFlags = MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI;
+
+ if (getSciVersion() >= SCI_VERSION_2_1)
+ deviceFlags |= MDT_PREFER_GM;
+
+ // Currently our CMS implementation only supports SCI1(.1)
+ if (getSciVersion() >= SCI_VERSION_1_EGA && getSciVersion() <= SCI_VERSION_1_1)
+ deviceFlags |= MDT_CMS;
+
+ uint32 dev = MidiDriver::detectDevice(deviceFlags);
+ _musicType = MidiDriver::getMusicType(dev);
+
+ switch (_musicType) {
case MT_ADLIB:
// FIXME: There's no Amiga sound option, so we hook it up to AdLib
if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh)
@@ -81,8 +93,11 @@ void SciMusic::init() {
case MT_PCSPK:
_pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion);
break;
+ case MT_CMS:
+ _pMidiDrv = MidiPlayer_CMS_create(_soundVersion);
+ break;
default:
- if (ConfMan.getBool("enable_fb01"))
+ if (ConfMan.getBool("native_fb01"))
_pMidiDrv = MidiPlayer_Fb01_create(_soundVersion);
else
_pMidiDrv = MidiPlayer_Midi_create(_soundVersion);
@@ -100,6 +115,7 @@ void SciMusic::init() {
// Find out what the first possible channel is (used, when doing channel
// remapping).
_driverFirstChannel = _pMidiDrv->getFirstChannel();
+ _driverLastChannel = _pMidiDrv->getLastChannel();
}
void SciMusic::miditimerCallback(void *p) {
@@ -260,6 +276,7 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this);
pSnd->pMidiParser->setMidiDriver(_pMidiDrv);
pSnd->pMidiParser->setTimerRate(_dwTempo);
+ pSnd->pMidiParser->setMasterVolume(_masterVolume);
}
pSnd->pauseCounter = 0;
@@ -288,6 +305,8 @@ int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) {
}
// otherwise look for unused channel
for (int channelNr = _driverFirstChannel; channelNr < 15; channelNr++) {
+ if (channelNr == 9) // never map to channel 9 (precussion)
+ continue;
if (!_usedChannel[channelNr]) {
_usedChannel[channelNr] = caller;
return channelNr;
@@ -349,20 +368,25 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
}
}
- if (pSnd->pStreamAud && !_pMixer->isSoundHandleActive(pSnd->hCurrentAud)) {
- if (pSnd->loop > 1) {
- pSnd->pLoopStream = new Audio::LoopingAudioStream(pSnd->pStreamAud,
- pSnd->loop, DisposeAfterUse::NO);
- _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud,
- 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);
+ if (pSnd->pStreamAud) {
+ if (!_pMixer->isSoundHandleActive(pSnd->hCurrentAud)) {
+ // Sierra SCI ignores volume set when playing samples via kDoSound
+ // At least freddy pharkas/CD has a script bug that sets volume to 0
+ // when playing the "score" sample
+ if (pSnd->loop > 1) {
+ pSnd->pLoopStream = new Audio::LoopingAudioStream(pSnd->pStreamAud,
+ pSnd->loop, DisposeAfterUse::NO);
+ _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud,
+ pSnd->pLoopStream, -1, _pMixer->kMaxChannelVolume, 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, _pMixer->kMaxChannelVolume, 0,
+ DisposeAfterUse::NO);
+ }
}
} else {
if (pSnd->pMidiParser) {
@@ -375,8 +399,14 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
if (pSnd->status == kSoundStopped) {
pSnd->pMidiParser->jumpToTick(0);
} else {
+ // Disable sound looping before fast forwarding to the last position,
+ // when loading a saved game. Fixes bug #3083151.
+ uint16 prevLoop = pSnd->loop;
+ pSnd->loop = 0;
// Fast forward to the last position and perform associated events when loading
pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ // Restore looping
+ pSnd->loop = prevLoop;
}
pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
@@ -412,7 +442,8 @@ void SciMusic::soundStop(MusicEntry *pSnd) {
void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
assert(volume <= MUSIC_VOLUME_MAX);
if (pSnd->pStreamAud) {
- _pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127
+ // we simply ignore volume changes for samples, because sierra sci also
+ // doesn't support volume for samples via kDoSound
} else if (pSnd->pMidiParser) {
_mutex.lock();
pSnd->pMidiParser->mainThreadBegin();
@@ -422,6 +453,13 @@ void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
}
}
+// this is used to set volume of the sample, used for fading only!
+void SciMusic::soundSetSampleVolume(MusicEntry *pSnd, byte volume) {
+ assert(volume <= MUSIC_VOLUME_MAX);
+ assert(pSnd->pStreamAud);
+ _pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127
+}
+
void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) {
Common::StackLock lock(_mutex);
@@ -525,8 +563,11 @@ void SciMusic::soundSetMasterVolume(uint16 vol) {
Common::StackLock lock(_mutex);
- if (_pMidiDrv)
- _pMidiDrv->setVolume(vol);
+ const MusicList::iterator end = _playList.end();
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+ if ((*i)->pMidiParser)
+ (*i)->pMidiParser->setMasterVolume(vol);
+ }
}
void SciMusic::sendMidiCommand(uint32 cmd) {
@@ -681,4 +722,23 @@ void MusicEntry::doFade() {
}
}
+void MusicEntry::setSignal(int newSignal) {
+ // For SCI0, we cache the signals to set, as some songs might
+ // update their signal faster than kGetEvent is called (which is where
+ // we manually invoke kDoSoundUpdateCues for SCI0 games). SCI01 and
+ // newer handle signalling inside kDoSoundUpdateCues. Refer to bug #3042981
+ if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
+ if (!signal) {
+ signal = newSignal;
+ } else {
+ // signal already set and waiting for getting to scripts, queue new one
+ signalQueue.push_back(newSignal);
+ }
+ } else {
+ // Set the signal directly for newer games, otherwise the sound
+ // object might be deleted already later on (refer to bug #3045913)
+ signal = newSignal;
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 3cf600fcf3..9fcbb9346d 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -47,6 +47,8 @@ enum SoundStatus {
#define MUSIC_VOLUME_DEFAULT 127
#define MUSIC_VOLUME_MAX 127
+#define MUSIC_MASTERVOLUME_DEFAULT 15
+#define MUSIC_MASTERVOLUME_MAX 15
class MidiParser_SCI;
class SegManager;
@@ -109,6 +111,7 @@ public:
void doFade();
void onTimer();
+ void setSignal(int signal);
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -145,6 +148,7 @@ public:
void soundResume(MusicEntry *pSnd);
void soundToggle(MusicEntry *pSnd, bool pause);
void soundSetVolume(MusicEntry *pSnd, byte volume);
+ void soundSetSampleVolume(MusicEntry *pSnd, byte volume);
void soundSetPriority(MusicEntry *pSnd, byte prio);
uint16 soundGetMasterVolume();
void soundSetMasterVolume(uint16 vol);
@@ -152,6 +156,7 @@ public:
void soundSetSoundOn(bool soundOnFlag);
uint16 soundGetVoices();
uint32 soundGetTempo() const { return _dwTempo; }
+ MusicType soundGetMusicType() const { return _musicType; }
bool soundIsActive(MusicEntry *pSnd) {
assert(pSnd->pStreamAud != 0);
@@ -215,8 +220,10 @@ private:
MusicEntry *_usedChannel[16];
MidiCommandQueue _queuedCommands;
+ MusicType _musicType;
int _driverFirstChannel;
+ int _driverLastChannel;
};
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index 567a1605f3..790164cf41 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -78,8 +78,13 @@ void SoundCommandParser::processInitSound(reg_t obj) {
// a relevant audio resource, play it, otherwise switch to synthesized
// effects. If the resource exists, play it using map 65535 (sound
// effects map)
+ bool checkAudioResource = getSciVersion() >= SCI_VERSION_1_1;
+ if (g_sci->getGameId() == GID_HOYLE4)
+ checkAudioResource = false; // hoyle 4 has garbled audio resources in place of the sound resources
+ // if we play those, we will only make the user deaf and break speakers. Sierra SCI doesn't play anything
+ // on soundblaster. FIXME: check, why this is
- if (getSciVersion() >= SCI_VERSION_1_1 && _resMan->testResource(ResourceId(kResourceTypeAudio, resourceId))) {
+ if (checkAudioResource && _resMan->testResource(ResourceId(kResourceTypeAudio, resourceId))) {
// Found a relevant audio resource, play it
int sampleLen;
newSound->pStreamAud = _audio->getAudioStream(resourceId, 65535, &sampleLen);
@@ -284,8 +289,8 @@ reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc)
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;
+ int vol = CLIP<int16>(argv[0].toSint16(), 0, MUSIC_MASTERVOLUME_MAX);
+ vol = vol * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX;
ConfMan.setInt("music_volume", vol);
ConfMan.setInt("sfx_volume", vol);
g_engine->syncSoundSettings();
@@ -298,7 +303,7 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ debugC(2, kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
return acc;
}
@@ -402,7 +407,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) {
}
// We get a flag from MusicEntry::doFade() here to set volume for the stream
if (musicSlot->fadeSetVolume) {
- _music->soundSetVolume(musicSlot, musicSlot->volume);
+ _music->soundSetSampleVolume(musicSlot, musicSlot->volume);
musicSlot->fadeSetVolume = false;
}
} else if (musicSlot->pMidiParser) {
@@ -688,6 +693,7 @@ void SoundCommandParser::startNewSound(int number) {
}
void SoundCommandParser::setMasterVolume(int vol) {
+ // 0...15
_music->soundSetMasterVolume(vol);
}
@@ -695,4 +701,9 @@ void SoundCommandParser::pauseAll(bool pause) {
_music->pauseAll(pause);
}
+MusicType SoundCommandParser::getMusicType() const {
+ assert(_music);
+ return _music->soundGetMusicType();
+}
+
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 8e6fb81762..61371d903f 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -27,6 +27,7 @@
#define SCI_SOUNDCMD_H
#include "common/list.h"
+#include "sound/mididrv.h" // for MusicType
#include "sci/engine/state.h"
namespace Sci {
@@ -47,10 +48,6 @@ public:
SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion);
~SoundCommandParser();
- enum {
- kMaxSciVolume = 15
- };
-
//reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
// Functions used for game state loading
@@ -71,6 +68,8 @@ public:
void processPlaySound(reg_t obj);
void processStopSound(reg_t obj, bool sampleFinishedPlaying);
+ MusicType getMusicType() const;
+
/**
* 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
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index a06939dc51..1bcb065b25 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2159,7 +2159,12 @@ void ScummEngine::stopTalk() {
((ScummEngine_v7 *)this)->clearSubtitleQueue();
#endif
} else {
- restoreCharsetBg();
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns)
+ towns_restoreCharsetBg();
+ else
+#endif
+ restoreCharsetBg();
}
}
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index fa4804ce7d..5b33fee742 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -52,6 +52,9 @@ void ScummEngine::loadCJKFont() {
_newLineCharacter = 0;
if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji
+#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE
+ error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build");
+#endif
int numChar = 256 * 32;
_2byteWidth = 16;
_2byteHeight = 16;
@@ -453,21 +456,51 @@ void CharsetRendererV3::setCurID(int32 id) {
}
int CharsetRendererCommon::getFontHeight() {
- if (_vm->_useCJKMode)
- return MAX(_vm->_2byteHeight + 1, _fontHeight);
- else
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ static const uint8 sjisFontHeightM1[] = { 0, 9, 10, 9, 10, 9, 10, 0, 0 };
+ static const uint8 sjisFontHeightM2[] = { 8, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
+ static const uint8 sjisFontHeightI4[] = { 8, 8, 9, 9, 9, 8, 8, 8, 8, 8 };
+ const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2);
+ return htbl[_curId];
+ } else {
+ return MAX(_vm->_2byteHeight + 1, _fontHeight);
+ }
+ } else
return _fontHeight;
}
// do spacing for variable width old-style font
-int CharsetRendererClassic::getCharWidth(byte chr) {
- if (chr >= 0x80 && _vm->_useCJKMode)
- return _vm->_2byteWidth / 2;
+int CharsetRendererClassic::getCharWidth(uint16 chr) {
int spacing = 0;
- int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
- if (offs) {
- spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if (_vm->_useCJKMode) {
+ if ((chr & 0xff00) == 0xfd00) {
+ chr &= 0xff;
+ } else if (chr >= 256) {
+ spacing = 9;
+ } else if (chr >= 128) {
+ spacing = 5;
+ }
+
+ if (spacing) {
+ static const uint8 sjisWidthM1[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const uint8 sjisWidthM2[] = { 0, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
+ static const uint8 sjisWidthI4[] = { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 };
+ const uint8 *wtbl = (_vm->_game.id == GID_MONKEY) ? sjisWidthM1 : ((_vm->_game.id == GID_INDY4) ? sjisWidthI4 : sjisWidthM2);
+ spacing += wtbl[_curId];
+ }
+ }
+ } else if (chr >= 0x80 && _vm->_useCJKMode) {
+ return _vm->_2byteWidth / 2;
+ }
+
+ if (!spacing) {
+ int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+ if (offs) {
+ spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
+ }
}
return spacing;
@@ -476,7 +509,7 @@ int CharsetRendererClassic::getCharWidth(byte chr) {
int CharsetRenderer::getStringWidth(int arg, const byte *text) {
int pos = 0;
int width = 1;
- byte chr;
+ uint16 chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -534,12 +567,18 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) {
}
}
}
- if ((chr & 0x80) && _vm->_useCJKMode) {
- pos++;
- width += _vm->_2byteWidth;
- } else {
- width += getCharWidth(chr);
+
+ if (_vm->_useCJKMode) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ if ((chr >= 0x80 && chr <= 0x9f) || (chr >= 0xe0 && chr <= 0xfd))
+ chr = (chr << 8) | text[pos++];
+ } else if (chr & 0x80) {
+ pos++;
+ width += _vm->_2byteWidth;
+ continue;
+ }
}
+ width += getCharWidth(chr);
}
setCurID(oldID);
@@ -631,7 +670,7 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
setCurID(oldID);
}
-int CharsetRendererV3::getCharWidth(byte chr) {
+int CharsetRendererV3::getCharWidth(uint16 chr) {
if (chr & 0x80 && _vm->_useCJKMode)
return _vm->_2byteWidth / 2;
int spacing = 0;
@@ -655,6 +694,14 @@ void CharsetRendererV3::setColor(byte color) {
} else
useShadow = false;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ _color = (_color & 0x0f) | ((_color & 0x0f) << 4);
+ if (_color == 0)
+ _color = 0x88;
+ }
+#endif
+
enableShadow(useShadow);
translateColor();
@@ -672,7 +719,12 @@ void CharsetRendererPCE::setColor(byte color) {
void CharsetRendererCommon::enableShadow(bool enable) {
if (enable) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
- _shadowColor = 8;
+ _shadowColor =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
+#else
+ 8;
+#endif
_shadowMode = kFMTOWNSShadowMode;
} else {
_shadowColor = 0;
@@ -683,7 +735,6 @@ void CharsetRendererCommon::enableShadow(bool enable) {
}
}
-
void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
// WORKAROUND for bug #1509509: Indy3 Mac does not show black
// characters (such as in the grail diary) if ignoreCharsetMask
@@ -724,8 +775,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
origHeight = height;
if (_shadowMode != kNoShadowMode) {
- width++;
- height++;
+ width += _vm->_textSurfaceMultiplier;
+ height += _vm->_textSurfaceMultiplier;
}
if (_firstChar) {
@@ -744,7 +795,12 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
_hasMask = true;
_textScreenID = vs->number;
}
- if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {
+
+ if (
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ (_vm->_game.platform != Common::kPlatformFMTowns || (_vm->_game.id == GID_LOOM && !is2byte)) &&
+#endif
+ (ignoreCharsetMask || !vs->hasTwoBuffers)) {
dst = vs->getPixels(_left, drawTop);
drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
} else {
@@ -801,6 +857,29 @@ void CharsetRenderer::translateColor() {
}
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+void CharsetRenderer::processTownsCharsetColors(uint8 bytesPerPixel) {
+ if (_vm->_game.platform == Common::kPlatformFMTowns) {
+ for (int i = 0; i < (1 << bytesPerPixel); i++) {
+ uint8 c = _vm->_charsetColorMap[i];
+
+ if (c > 16) {
+ uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12;
+ t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10);
+ t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9);
+ c = t;
+ }
+
+ if (c == 0)
+ c = _vm->_townsOverrideShadowColor;
+
+ c = ((c & 0x0f) << 4) | (c & 0x0f);
+ _vm->_townsCharsetColorMap[i] = c;
+ }
+ }
+}
+#endif
+
void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {
static const SaveLoadEntry charsetRendererEntries[] = {
MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)),
@@ -836,6 +915,10 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_vm->_charsetColorMap[1] = _color;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ processTownsCharsetColors(_bytesPerPixel);
+#endif
+
if (is2byte) {
enableShadow(true);
charPtr = _vm->get2byteCharPtr(chr);
@@ -851,7 +934,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
width = charPtr[0];
height = charPtr[1];
-
+
if (_disableOffsX) {
offsX = 0;
} else {
@@ -866,8 +949,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
origHeight = height;
if (_shadowMode != kNoShadowMode) {
- width++;
- height++;
+ width += _vm->_textSurfaceMultiplier;
+ height += _vm->_textSurfaceMultiplier;
}
if (_firstChar) {
_str.left = 0;
@@ -905,7 +988,13 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
- if (!ignoreCharsetMask) {
+ // This check for kPlatformFMTowns and kMainVirtScreen is at least required for the chat with
+ // the navigator's head in front of the ghost ship in Monkey Island 1
+ if (!ignoreCharsetMask
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)
+#endif
+ ) {
_hasMask = true;
_textScreenID = vs->number;
}
@@ -961,7 +1050,11 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,
} else {
Graphics::Surface dstSurface;
Graphics::Surface backSurface;
- if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {
+ if ((ignoreCharsetMask || !vs->hasTwoBuffers)
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ && (_vm->_game.platform != Common::kPlatformFMTowns)
+#endif
+ ) {
dstSurface = *vs;
dstPtr = vs->getPixels(_left, drawTop);
} else {
@@ -1064,13 +1157,18 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
bits = *src++;
numbits = 8;
+ byte *cmap =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap :
+#endif
+ _vm->_charsetColorMap;
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
color = (bits >> (8 - bpp)) & 0xFF;
if (color && y + drawTop >= 0) {
- *dst = _vm->_charsetColorMap[color];
+ *dst = cmap[color];
}
dst++;
bits <<= bpp;
@@ -1087,6 +1185,11 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co
void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {
int y, x;
byte bits = 0;
+ uint8 col =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] :
+#endif
+ _color;
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
@@ -1108,7 +1211,7 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con
if (_shadowMode != kFMTOWNSShadowMode)
*(dst + s.pitch + 1) = _shadowColor;
}
- *dst = _color;
+ *dst = col;
}
}
dst += bitDepth;
@@ -1191,7 +1294,7 @@ int CharsetRendererNut::getCharHeight(byte chr) {
return _current->getCharHeight(chr);
}
-int CharsetRendererNut::getCharWidth(byte chr) {
+int CharsetRendererNut::getCharWidth(uint16 chr) {
assert(_current);
return _current->getCharWidth(chr);
}
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index dca254669b..b5fc7b1b15 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -80,12 +80,16 @@ public:
void addLinebreaks(int a, byte *str, int pos, int maxwidth);
void translateColor();
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ void processTownsCharsetColors(uint8 bytesPerPixel);
+#endif
+
virtual void setCurID(int32 id) = 0;
int getCurID() { return _curId; }
virtual int getFontHeight() = 0;
virtual int getCharHeight(byte chr) { return getFontHeight(); }
- virtual int getCharWidth(byte chr) = 0;
+ virtual int getCharWidth(uint16 chr) = 0;
virtual void setColor(byte color) { _color = color; translateColor(); }
@@ -130,7 +134,7 @@ public:
void printChar(int chr, bool ignoreCharsetMask);
void drawChar(int chr, const Graphics::Surface &s, int x, int y);
- int getCharWidth(byte chr);
+ int getCharWidth(uint16 chr);
};
class CharsetRendererNES : public CharsetRendererCommon {
@@ -147,7 +151,7 @@ public:
void drawChar(int chr, const Graphics::Surface &s, int x, int y);
int getFontHeight() { return 8; }
- int getCharWidth(byte chr) { return 8; }
+ int getCharWidth(uint16 chr) { return 8; }
};
class CharsetRendererV3 : public CharsetRendererCommon {
@@ -161,7 +165,7 @@ public:
void drawChar(int chr, const Graphics::Surface &s, int x, int y);
void setCurID(int32 id);
void setColor(byte color);
- int getCharWidth(byte chr);
+ int getCharWidth(uint16 chr);
};
#ifdef USE_RGB_COLOR
@@ -185,7 +189,7 @@ public:
~CharsetRendererV2();
void setCurID(int32 id) {}
- int getCharWidth(byte chr) { return 8; }
+ int getCharWidth(uint16 chr) { return 8; }
};
#ifdef ENABLE_SCUMM_7_8
@@ -204,7 +208,7 @@ public:
int getFontHeight();
int getCharHeight(byte chr);
- int getCharWidth(byte chr);
+ int getCharWidth(uint16 chr);
};
#endif
diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
index b1f8f2ae2b..8e211a5041 100644
--- a/engines/scumm/cursor.cpp
+++ b/engines/scumm/cursor.cpp
@@ -554,11 +554,16 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {
uint16 color;
const uint16 *src = _cursorImages[_currentCursor];
- if (_bytesPerPixel == 2) {
+ if (_bytesPerPixelOutput == 2) {
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
byte r, g, b;
colorPCEToRGB(default_pce_cursor_colors[idx], &r, &g, &b);
color = get16BitColor(r, g, b);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ } else if (_game.platform == Common::kPlatformFMTowns) {
+ byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3];
+ color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]);
+#endif
} else {
color = _16BitPalette[default_cursor_colors[idx]];
}
@@ -570,18 +575,28 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {
memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));
}
- _cursor.hotspotX = _cursorHotspots[2 * _currentCursor];
- _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1];
- _cursor.width = 16;
- _cursor.height = 16;
+ _cursor.hotspotX = _cursorHotspots[2 * _currentCursor] * _textSurfaceMultiplier;
+ _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1] * _textSurfaceMultiplier;
+ _cursor.width = 16 * _textSurfaceMultiplier;
+ _cursor.height = 16 * _textSurfaceMultiplier;
+
+ int scl = _bytesPerPixelOutput * _textSurfaceMultiplier;
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
if (src[i] & (1 << j)) {
- if (_bytesPerPixel == 2)
- WRITE_UINT16(_grabbedCursor + 32 * i + (15 - j) * 2, color);
- else
- _grabbedCursor[16 * i + 15 - j] = color;
+ byte *dst1 = _grabbedCursor + 16 * scl * i * _textSurfaceMultiplier + (15 - j) * scl;
+ byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1;
+ if (_bytesPerPixelOutput == 2) {
+ for (int b = 0; b < scl; b += 2) {
+ *((uint16*)dst1) = *((uint16*)dst2) = color;
+ dst1 += 2;
+ dst2 += 2;
+ }
+ } else {
+ for (int b = 0; b < scl; b++)
+ *dst1++ = *dst2++ = color;
+ }
}
}
}
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 9721c75677..9010cb84c3 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -401,8 +401,8 @@ static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map
continue;
bool matched = false;
- for (const char *glob = *globs; *glob; glob++)
- if (file->getName().matchString(glob, true)) {
+ for (const char **glob = globs; *glob; glob++)
+ if (file->getName().matchString(*glob, true)) {
matched = true;
break;
}
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index f275b1c93f..4428185774 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -186,6 +186,7 @@ using Common::GUIO_NONE;
using Common::GUIO_NOLAUNCHLOAD;
using Common::GUIO_NOMIDI;
using Common::GUIO_NOSPEECH;
+using Common::GUIO_MIDITOWNS;
// The following table contains information about variants of our various
// games. We index into it with help of md5table (from scumm-md5.h), to find
@@ -217,19 +218,20 @@ static const GameSettings gameVariantsTable[] = {
{"zak", "V1", "v1", GID_ZAK, 1, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"zak", "V2", "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"zak", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
+ {"zak", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
+
{"indy3", "EGA", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"indy3", "VGA", "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | 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},
+ {"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"loom", "No AdLib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | 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},
#endif
- {"loom", "FM-TOWNS", 0, GID_LOOM, 3, 0, MDT_TOWNS, GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
+ {"loom", "FM-TOWNS", 0, GID_LOOM, 3, 0, MDT_TOWNS, GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"loom", "VGA", "vga", GID_LOOM, 4, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
@@ -239,13 +241,15 @@ static const GameSettings gameVariantsTable[] = {
{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR, GF_16COLOR, Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | 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", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"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 | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH},
- {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
+ {"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},
+ {"atlantis", "FM-TOWNS", 0, GID_INDY4, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NONE},
{"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},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 1e0bf6d4be..a48e54c05a 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -473,14 +473,25 @@ void PauseDialog::handleKeyDown(Common::KeyState state) {
}
ConfirmDialog::ConfirmDialog(ScummEngine *scumm, int res)
- : InfoDialog(scumm, res) {
+ : InfoDialog(scumm, res), _yesKey('y'), _noKey('n') {
+
+ if (_message.lastChar() != ')') {
+ _yesKey = _message.lastChar();
+ _message.deleteLastChar();
+
+ if (_yesKey >= 'A' && _yesKey <= 'Z')
+ _yesKey += 'a' - 'A';
+
+ _text->setLabel(_message);
+ reflowLayout();
+ }
}
void ConfirmDialog::handleKeyDown(Common::KeyState state) {
- if (state.keycode == Common::KEYCODE_n) {
+ if (state.keycode == Common::KEYCODE_n || state.ascii == _noKey) {
setResult(0);
close();
- } else if (state.keycode == Common::KEYCODE_y) {
+ } else if (state.keycode == Common::KEYCODE_y || state.ascii == _yesKey) {
setResult(1);
close();
} else
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index 41a8ec83c1..0e6e18905f 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -118,6 +118,9 @@ class ConfirmDialog : public InfoDialog {
public:
ConfirmDialog(ScummEngine *scumm, int res);
virtual void handleKeyDown(Common::KeyState state);
+
+protected:
+ char _yesKey, _noKey;
};
/**
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index 7b0d4909d6..e7c81bd418 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -51,7 +51,6 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height, uint8
static void clear8Col(byte *dst, int dstPitch, int height, uint8 bitDepth);
static void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height);
-static void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h);
struct StripTable {
int offsets[160];
@@ -323,6 +322,18 @@ void ScummEngine::initScreens(int b, int h) {
_res->nukeResource(rtBuffer, i + 5);
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen) {
+ if (!_townsClearLayerFlag && (h - b != _virtscr[kMainVirtScreen].h))
+ _townsScreen->clearLayer(0);
+
+ if (_game.id != GID_MONKEY) {
+ _textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0);
+ _townsScreen->clearLayer(1);
+ }
+ }
+#endif
+
if (!getResourceAddress(rtBuffer, 4)) {
// Since the size of screen 3 is fixed, there is no need to reallocate
// it if its size changed.
@@ -611,16 +622,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
int m = _textSurfaceMultiplier;
int vsPitch;
int pitch = vs->pitch;
-
- if (_useCJKMode && _textSurfaceMultiplier == 2) {
- scale2x(_fmtownsBuf, _screenWidth * m, (const byte *)src, vs->pitch, width, height);
- src = _fmtownsBuf;
-
- vsPitch = _screenWidth * m - width * m;
-
- } else {
- vsPitch = vs->pitch - width * vs->bytesPerPixel;
- }
+ vsPitch = vs->pitch - width * vs->bytesPerPixel;
if (_game.version < 7) {
@@ -643,7 +645,13 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i
#ifdef USE_ARM_GFX_ASM
asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch);
#else
- if (_bytesPerPixel == 2) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ towns_drawStripToScreen(vs, x, y, x, top, width, height);
+ return;
+ } else
+#endif
+ if (_bytesPerPixelOutput == 2) {
const byte *srcPtr = (const byte *)src;
const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m);
byte *dstPtr = _compositeBuf;
@@ -824,28 +832,6 @@ void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *wid
*height = dsty - *y;
}
-void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
- /* dst and dstPitch should both be even. So the use of (void *) in
- * the following casts to avoid the unnecessary warning is valid. */
- uint16 *dstL1 = (uint16 *)(void *)dst;
- uint16 *dstL2 = (uint16 *)(void *)(dst + dstPitch);
-
- const int dstAdd = dstPitch - w;
- const int srcAdd = srcPitch - w;
-
- while (h--) {
- for (int x = 0; x < w; ++x) {
- uint16 col = *src++;
- col |= col << 8;
- *dstL1++ = col;
- *dstL2++ = col;
- }
- dstL1 += dstAdd; dstL2 += dstAdd;
- src += srcAdd;
- }
-}
-
-
#pragma mark -
#pragma mark --- Background buffers & charset mask ---
#pragma mark -
@@ -1017,7 +1003,7 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
VirtScreen *vs;
byte *screenBuf;
- if (rect.top < 0)
+ if (rect.top < 0)
rect.top = 0;
if (rect.left >= rect.right || rect.top >= rect.bottom)
return;
@@ -1028,6 +1014,11 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
if (rect.left > vs->w)
return;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154)
+ rect.right = 320;
+#endif
+
// Convert 'rect' to local (virtual screen) coordinates
rect.top -= vs->topline;
rect.bottom -= vs->topline;
@@ -1047,10 +1038,26 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) {
blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height, vs->bytesPerPixel);
if (vs->number == kMainVirtScreen && _charset->_hasMask) {
- byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop);
- fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height, _textSurface.bytesPerPixel);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier);
+ fill(mask, _textSurface.pitch, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+ } else
+#endif
+ {
+ byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop);
+ fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+ }
}
} else {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ backColor |= (backColor << 4);
+ byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier);
+ fill(mask, _textSurface.pitch, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+ }
+#endif
+
if (_game.features & GF_16BIT_COLOR)
fill(screenBuf, vs->pitch, _16BitPalette[backColor], width, height, vs->bytesPerPixel);
else
@@ -1102,7 +1109,16 @@ void ScummEngine::clearCharsetMask() {
}
void ScummEngine::clearTextSurface() {
- fill((byte*)_textSurface.pixels, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0);
+#endif
+
+ fill((byte*)_textSurface.pixels, _textSurface.pitch,
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _game.platform == Common::kPlatformFMTowns ? 0 :
+#endif
+ CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel);
}
byte *ScummEngine::getMaskBuffer(int x, int y, int z) {
@@ -1256,13 +1272,32 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
backbuff = vs->getPixels(x, y);
bgbuff = vs->getBackPixels(x, y);
- if (color == -1) {
- if (vs->number != kMainVirtScreen)
- error("can only copy bg to main window");
- blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel);
- if (_charset->_hasMask) {
- byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier);
- fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+ // A check for -1 might be wrong in all cases since o5_drawBox() in its current form
+ // is definitely not capable of passing a parameter of -1 (color range is 0 - 255).
+ // Just to make sure I don't break anything I restrict the code change to FM-Towns
+ // version 5 games where this change is necessary to fix certain long standing bugs.
+ if (color == -1
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ || (color >= 254 && _game.platform == Common::kPlatformFMTowns && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4))
+#endif
+ ) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ if (color == 254) {
+ color = color;
+ towns_setupPalCycleField(x, y, x2, y2);
+ }
+ } else
+#endif
+ {
+ if (vs->number != kMainVirtScreen)
+ error("can only copy bg to main window");
+
+ blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel);
+ if (_charset->_hasMask) {
+ byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier);
+ fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+ }
}
} else if (_game.heversion >= 72) {
// Flags are used for different methods in HE games
@@ -1293,10 +1328,22 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
fill(backbuff, vs->pitch, flags, width, height, vs->bytesPerPixel);
}
} else {
- if (_game.features & GF_16BIT_COLOR)
+ if (_game.features & GF_16BIT_COLOR) {
fill(backbuff, vs->pitch, _16BitPalette[color], width, height, vs->bytesPerPixel);
- else
+ } else {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ color = ((color & 0x0f) << 4) | (color & 0x0f);
+ byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop + vs->topline) * _textSurfaceMultiplier);
+ fill(mask, _textSurface.pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel);
+
+ if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4 || ((_game.id == GID_INDY3 || _game.id == GID_ZAK) && vs->number != kTextVirtScreen) || (_game.id == GID_LOOM && vs->number == kMainVirtScreen))
+ return;
+ }
+#endif
+
fill(backbuff, vs->pitch, color, width, height, vs->bytesPerPixel);
+ }
}
}
@@ -1703,6 +1750,13 @@ void Gdi::drawBitmap(const byte *ptr, VirtScreen *vs, int x, const int y, const
warning("Gdi::drawBitmap, strip drawn to %d below window bottom %d", y + height, vs->h);
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_vm->_townsPaletteFlags & 2) {
+ int cx = (x - _vm->_screenStartStrip) << 3;
+ _vm->_textSurface.fillRect(Common::Rect(cx * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, (cx + width - 1) * _vm->_textSurfaceMultiplier, (y + height - 1) * _vm->_textSurfaceMultiplier), 0);
+ }
+#endif
+
_vertStripNextInc = height * vs->pitch - 1 * vs->bytesPerPixel;
_objectMode = (flag & dbObjectMode) == dbObjectMode;
@@ -1794,18 +1848,16 @@ bool Gdi::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width,
if (stripnr * 2 + 2 < smapLen) {
offset = READ_LE_UINT16(smap_ptr + stripnr * 2 + 2);
}
- assertRange(0, offset, smapLen-1, "screen strip");
} else if (_vm->_game.features & GF_SMALL_HEADER) {
smapLen = READ_LE_UINT32(smap_ptr);
if (stripnr * 4 + 4 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 4);
- assertRange(0, offset, smapLen-1, "screen strip");
} else {
smapLen = READ_BE_UINT32(smap_ptr);
if (stripnr * 4 + 8 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 8);
- assertRange(0, offset, smapLen-1, "screen strip");
}
+ assertRange(0, offset, smapLen-1, "screen strip");
return decompressBitmap(dstPtr, vs->pitch, smap_ptr + offset, height);
}
@@ -1902,7 +1954,7 @@ void Gdi::decodeMask(int x, int y, const int width, const int height,
z_plane_ptr = zplane_list[i] + offs;
if (tmsk_ptr) {
- const byte *tmsk = tmsk_ptr + READ_LE_UINT16(tmsk_ptr + 8);
+ const byte *tmsk = tmsk_ptr + READ_LE_UINT16(tmsk_ptr + stripnr * 2 + 8);
decompressTMSK(mask_ptr, tmsk, z_plane_ptr, height);
} else if (transpStrip && (flag & dbAllowMaskOr)) {
decompressMaskImgOr(mask_ptr, z_plane_ptr, height);
@@ -3655,12 +3707,16 @@ void ScummEngine::fadeOut(int effect) {
if (_game.version < 7)
camera._last.x = camera._cur.x;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.version == 3 && _game.platform == Common::kPlatformFMTowns)
+ _textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0);
+#endif
+
// TheDig can disable fadeIn(), and may call fadeOut() several times
// successively. Disabling the _screenEffectFlag check forces the screen
// to get cleared. This fixes glitches, at least, in the first cutscenes
// when bypassed of FT and TheDig.
if ((_game.version == 7 || _screenEffectFlag) && effect != 0) {
-
// Fill screen 0 with black
memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h);
@@ -3858,15 +3914,12 @@ void ScummEngine::dissolveEffect(int width, int height) {
x = offsets[i] % vs->pitch;
y = offsets[i] / vs->pitch;
- if (_useCJKMode && _textSurfaceMultiplier == 2) {
- int m = _textSurfaceMultiplier;
- byte *dst = _fmtownsBuf + x * m + y * m * _screenWidth * m;
- scale2x(dst, _screenWidth * m, vs->getPixels(x, y), vs->pitch, width, height);
-
- _system->copyRectToScreen(dst, _screenWidth * m, x * m, (y + vs->topline) * m, width * m, height * m);
- } else {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns)
+ towns_drawStripToScreen(vs, x, y + vs->topline, x, y, width, height);
+ else
+#endif
_system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
- }
if (++blits >= blits_before_refresh) {
@@ -3906,23 +3959,21 @@ void ScummEngine::scrollEffect(int dir) {
y = 1 + step;
while (y < vs->h) {
moveScreen(0, -step, vs->h);
-
- src = vs->getPixels(0, y - step);
- if (_useCJKMode && m == 2) {
- int x1 = 0, y1 = vs->h - step;
- byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
- scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
- src = dst;
- vsPitch = _screenWidth * 2;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen) {
+ towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step);
+ } else
+#endif
+ {
+ src = vs->getPixels(0, y - step);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ 0, (vs->h - step) * m,
+ vs->w * m, step * m);
+ _system->updateScreen();
}
-
- _system->copyRectToScreen(src,
- vsPitch,
- 0 * m, (vs->h - step) * m,
- vs->w * m, step * m);
- _system->updateScreen();
+
waitForTimer(delay);
-
y += step;
}
break;
@@ -3931,21 +3982,21 @@ void ScummEngine::scrollEffect(int dir) {
y = 1 + step;
while (y < vs->h) {
moveScreen(0, step, vs->h);
- src = vs->getPixels(0, vs->h - y);
- if (_useCJKMode && m == 2) {
- int x1 = 0, y1 = 0;
- byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
- scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step);
- src = dst;
- vsPitch = _screenWidth * 2;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen) {
+ towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step);
+ } else
+#endif
+ {
+ src = vs->getPixels(0, vs->h - y);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ 0, 0,
+ vs->w * m, step * m);
+ _system->updateScreen();
}
- _system->copyRectToScreen(src,
- vsPitch,
- 0, 0,
- vs->w * m, step * m);
- _system->updateScreen();
+
waitForTimer(delay);
-
y += step;
}
break;
@@ -3954,21 +4005,22 @@ void ScummEngine::scrollEffect(int dir) {
x = 1 + step;
while (x < vs->w) {
moveScreen(-step, 0, vs->h);
- src = vs->getPixels(x - step, 0);
- if (_useCJKMode && m == 2) {
- int x1 = vs->w - step, y1 = 0;
- byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
- scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
- src = dst;
- vsPitch = _screenWidth * 2;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen) {
+ towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h);
+ } else
+#endif
+ {
+ src = vs->getPixels(x - step, 0);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ (vs->w - step) * m, 0,
+ step * m, vs->h * m);
+ _system->updateScreen();
}
- _system->copyRectToScreen(src,
- vsPitch,
- (vs->w - step) * m, 0,
- step * m, vs->h * m);
- _system->updateScreen();
- waitForTimer(delay);
+ waitForTimer(delay);
x += step;
}
break;
@@ -3977,21 +4029,22 @@ void ScummEngine::scrollEffect(int dir) {
x = 1 + step;
while (x < vs->w) {
moveScreen(step, 0, vs->h);
- src = vs->getPixels(vs->w - x, 0);
- if (_useCJKMode && m == 2) {
- int x1 = 0, y1 = 0;
- byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m;
- scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h);
- src = dst;
- vsPitch = _screenWidth * 2;
- }
- _system->copyRectToScreen(src,
- vsPitch,
- 0, 0,
- step, vs->h);
- _system->updateScreen();
- waitForTimer(delay);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen) {
+ towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h);
+ } else
+#endif
+ {
+ src = vs->getPixels(vs->w - x, 0);
+ _system->copyRectToScreen(src,
+ vsPitch,
+ 0, 0,
+ step, vs->h);
+ _system->updateScreen();
+ }
+
+ waitForTimer(delay);
x += step;
}
break;
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index cdb473a67c..c6062ef9be 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -26,6 +26,9 @@
#ifndef SCUMM_GFX_H
#define SCUMM_GFX_H
+#include "common/system.h"
+#include "common/list.h"
+
#include "graphics/surface.h"
namespace Scumm {
@@ -421,6 +424,66 @@ public:
};
#endif
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+// Helper class for FM-Towns output (required for specific hardware effects like
+// switching graphics layers on and off).
+class TownsScreen {
+public:
+ TownsScreen(OSystem *system, int width, int height, int bpp);
+ ~TownsScreen();
+
+ void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0);
+ void clearLayer(int layer);
+ void fillLayerRect(int layer, int x, int y, int w, int h, int col);
+ //void copyRectToLayer(int layer, int x, int y, int w, int h, const uint8 *src);
+
+ uint8 *getLayerPixels(int layer, int x, int y);
+ int getLayerPitch(int layer);
+ int getLayerHeight(int layer);
+ int getLayerBpp(int layer);
+ int getLayerScaleW(int layer);
+ int getLayerScaleH(int layer);
+
+ void addDirtyRect(int x, int y, int w, int h);
+ void toggleLayers(int flag);
+ void update();
+
+private:
+ void updateOutputBuffer();
+ void outputToScreen();
+ uint16 calc16BitColor(const uint8 *palEntry);
+
+ struct TownsScreenLayer {
+ uint8 *pixels;
+ uint8 *palette;
+ int pitch;
+ int height;
+ int bpp;
+ int numCol;
+ uint8 scaleW;
+ uint8 scaleH;
+ bool onBottom;
+ bool enabled;
+ bool ready;
+
+ uint16 *bltInternX;
+ uint8 **bltInternY;
+ uint16 *bltTmpPal;
+ } _layers[2];
+
+ uint8 *_outBuffer;
+
+ int _height;
+ int _width;
+ int _pitch;
+ int _bpp;
+
+ int _numDirtyRects;
+ Common::List<Common::Rect> _dirtyRects;
+ OSystem *_system;
+};
+#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
+
} // End of namespace Scumm
#endif
diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp
new file mode 100644
index 0000000000..33b1779b0b
--- /dev/null
+++ b/engines/scumm/gfx_towns.cpp
@@ -0,0 +1,522 @@
+/* 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/endian.h"
+
+#include "scumm/scumm.h"
+#include "scumm/charset.h"
+#include "scumm/util.h"
+#include "scumm/resource.h"
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+
+namespace Scumm {
+
+void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int width, int height) {
+ if (width <= 0 || height <= 0)
+ return;
+
+ assert(_textSurface.pixels);
+
+ int m = _textSurfaceMultiplier;
+
+ uint8 *src1 = vs->getPixels(srcX, srcY);
+ uint8 *src2 = (uint8*)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m);
+ uint8 *dst1 = _townsScreen->getLayerPixels(0, dstX, dstY);
+ uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m);
+
+ int dp1 = _townsScreen->getLayerPitch(0) - width * _townsScreen->getLayerBpp(0);
+ int dp2 = _townsScreen->getLayerPitch(1) - width * m * _townsScreen->getLayerBpp(1);
+ int sp1 = vs->pitch - (width * vs->bytesPerPixel);
+ int sp2 = _textSurface.pitch - width * m;
+
+ if (vs->number == kMainVirtScreen || _game.id == GID_INDY3 || _game.id == GID_ZAK) {
+ for (int h = 0; h < height; ++h) {
+ if (_bytesPerPixelOutput == 2) {
+ for (int w = 0; w < width; ++w) {
+ *(uint16*)dst1 = _16BitPalette[*src1++];
+ dst1 += _bytesPerPixelOutput;
+ }
+
+ src1 += sp1;
+ dst1 += dp1;
+ } else {
+ memcpy(dst1, src1, width);
+ src1 += vs->pitch;
+ dst1 += _townsScreen->getLayerPitch(0);
+ }
+
+ for (int sH = 0; sH < m; ++sH) {
+ memcpy(dst2, src2, width * m);
+ src2 += _textSurface.pitch;
+ dst2 += _townsScreen->getLayerPitch(1);
+ }
+ }
+ } else {
+ dst1 = dst2;
+ for (int h = 0; h < height; ++h) {
+ for (int w = 0; w < width; ++w) {
+ uint8 t = (*src1++) & 0x0f;
+ memset(dst1, (t << 4) | t, m);
+ dst1 += m;
+ }
+
+ dst1 = dst2;
+ uint8 *src3 = src2;
+
+ if (m == 2) {
+ dst2 += _townsScreen->getLayerPitch(1);
+ src3 += _townsScreen->getLayerPitch(1);
+ }
+
+ for (int w = 0; w < width * m; ++w) {
+ *dst2++ = (*src3 | (*dst1 & _townsLayer2Mask[*src3]));
+ *dst1 = (*src2 | (*dst1 & _townsLayer2Mask[*src2]));
+ src2++;
+ src3++;
+ dst1++;
+ }
+
+ src1 += sp1;
+ src2 = src3 + sp2;
+ dst1 = dst2 + dp2;
+ dst2 += dp2;
+ }
+ }
+
+ _townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m);
+}
+
+bool ScummEngine::towns_isRectInStringBox(int x1, int y1, int x2, int y2) {
+ if (_game.platform == Common::kPlatformFMTowns && _charset->_hasMask && y1 <= _curStringRect.bottom && x1 <= _curStringRect.right && y2 >= _curStringRect.top && x2 >= _curStringRect.left)
+ return true;
+ return false;
+}
+
+void ScummEngine::towns_restoreCharsetBg() {
+ if (_curStringRect.left != -1) {
+ restoreBackground(_curStringRect, 0);
+ _curStringRect.left = -1;
+ _charset->_hasMask = false;
+ _nextLeft = _string[0].xpos;
+ }
+
+ _nextLeft = _string[0].xpos;
+ _nextTop = _string[0].ypos;
+}
+
+#ifdef USE_RGB_COLOR
+void ScummEngine::towns_setPaletteFromPtr(const byte *ptr, int numcolor) {
+ setPaletteFromPtr(ptr, numcolor);
+
+ if (_game.version == 5)
+ towns_setTextPaletteFromPtr(_currentPalette);
+
+ _townsOverrideShadowColor = 1;
+ int m = 48;
+ for (int i = 1; i < 16; ++i) {
+ int val = _currentPalette[i * 3] + _currentPalette[i * 3 + 1] + _currentPalette[i * 3 + 2];
+ if (m > val) {
+ _townsOverrideShadowColor = i;
+ m = val;
+ }
+ }
+}
+
+void ScummEngine::towns_setTextPaletteFromPtr(const byte *ptr) {
+ memcpy(_textPalette, ptr, 48);
+}
+#endif
+
+void ScummEngine::towns_setupPalCycleField(int x1, int y1, int x2, int y2) {
+ if (_numCyclRects >= 10)
+ return;
+ _cyclRects[_numCyclRects].left = x1;
+ _cyclRects[_numCyclRects].top = y1;
+ _cyclRects[_numCyclRects].right = x2;
+ _cyclRects[_numCyclRects].bottom = y2;
+ _numCyclRects++;
+ _townsPaletteFlags |= 1;
+}
+
+void ScummEngine::towns_processPalCycleField() {
+ for (int i = 0; i < _numCyclRects; i++) {
+ int x1 = _cyclRects[i].left - _virtscr[kMainVirtScreen].xstart;
+ int x2 = _cyclRects[i].right - _virtscr[kMainVirtScreen].xstart;
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 > 320)
+ x2 = 320;
+ if (x2 > 0)
+ markRectAsDirty(kMainVirtScreen, x1, x2, _cyclRects[i].top, _cyclRects[i].bottom);
+ }
+}
+
+void ScummEngine::towns_resetPalCycleFields() {
+ _numCyclRects = 0;
+ _townsPaletteFlags &= ~1;
+}
+
+const uint8 ScummEngine::_townsLayer2Mask[] = {
+ 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#define DIRTY_RECTS_MAX 20
+#define FULL_REDRAW (DIRTY_RECTS_MAX + 1)
+
+TownsScreen::TownsScreen(OSystem *system, int width, int height, int bpp) :
+ _system(system), _width(width), _height(height), _bpp(bpp), _pitch(width * bpp) {
+ memset(&_layers[0], 0, sizeof(TownsScreenLayer));
+ memset(&_layers[1], 0, sizeof(TownsScreenLayer));
+ _outBuffer = new byte[_pitch * _height];
+ memset(_outBuffer, 0, _pitch * _height);
+
+ setupLayer(0, width, height, 256);
+}
+
+TownsScreen::~TownsScreen() {
+ delete[] _layers[0].pixels;
+ delete[] _layers[1].pixels;
+ delete[] _layers[0].bltInternX;
+ delete[] _layers[1].bltInternX;
+ delete[] _layers[0].bltInternY;
+ delete[] _layers[1].bltInternY;
+ delete[] _layers[0].bltTmpPal;
+ delete[] _layers[1].bltTmpPal;
+ delete[] _outBuffer;
+ _dirtyRects.clear();
+}
+
+void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void *pal) {
+ if (layer < 0 || layer > 1)
+ return;
+
+ TownsScreenLayer *l = &_layers[layer];
+
+ if (numCol >> 15)
+ error("TownsScreen::setupLayer(): No more than 32767 colors supported.");
+
+ if (width > _width || height > _height)
+ error("TownsScreen::setupLayer(): Layer width/height must be equal or less than screen width/height");
+
+ l->scaleW = _width / width;
+ l->scaleH = _height / height;
+
+ if ((float)l->scaleW != ((float)_width / (float)width) || (float)l->scaleH != ((float)_height / (float)height))
+ error("TownsScreen::setupLayer(): Layer width/height must be equal or an EXACT half, third, etc. of screen width/height.\n More complex aspect ratio scaling is not supported.");
+
+ if (width <= 0 || height <= 0 || numCol < 16)
+ error("TownsScreen::setupLayer(): Invalid width/height/number of colors setting.");
+
+ l->height = height;
+ l->numCol = numCol;
+ l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1;
+ l->pitch = width * l->bpp;
+ l->palette = (uint8*)pal;
+
+ if (l->palette && _bpp == 1)
+ warning("TownsScreen::setupLayer(): Layer palette usage requires 15 bit graphics setting.\nLayer palette will be ignored.");
+
+ delete[] l->pixels;
+ l->pixels = new uint8[l->pitch * l->height];
+ assert(l->pixels);
+ memset(l->pixels, 0, l->pitch * l->height);
+
+ // build offset tables to speed up merging/scaling layers
+ delete[] l->bltInternX;
+ l->bltInternX = new uint16[_width];
+ for (int i = 0; i < _width; ++i)
+ l->bltInternX[i] = (i / l->scaleW) * l->bpp;
+
+ delete[] l->bltInternY;
+ l->bltInternY = new uint8*[_height];
+ for (int i = 0; i < _height; ++i)
+ l->bltInternY[i] = l->pixels + (i / l->scaleH) * l->pitch;
+
+ delete[] l->bltTmpPal;
+ l->bltTmpPal = (l->bpp == 1 && _bpp == 2) ? new uint16[l->numCol] : 0;
+
+ l->enabled = true;
+ l->onBottom = (!layer || !_layers[0].enabled);
+ l->ready = true;
+}
+
+void TownsScreen::clearLayer(int layer) {
+ if (layer < 0 || layer > 1)
+ return;
+
+ TownsScreenLayer *l = &_layers[layer];
+ if (!l->ready)
+ return;
+
+ memset(l->pixels, 0, l->pitch * l->height);
+ _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+ _numDirtyRects = FULL_REDRAW;
+}
+
+
+void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) {
+ if (layer < 0 || layer > 1 || w <= 0 || h <= 0)
+ return;
+
+ TownsScreenLayer *l = &_layers[layer];
+ if (!l->ready)
+ return;
+
+ assert(x >= 0 && y >= 0 && ((x + w) * l->bpp) <= (l->pitch) && (y + h) <= (l->height));
+
+ uint8 *pos = l->pixels + y * l->pitch + x * l->bpp;
+
+ for (int i = 0; i < h; ++i) {
+ if (l->bpp == 2) {
+ for (int ii = 0; ii < w; ++ii) {
+ *(uint16*)pos = col;
+ pos += 2;
+ }
+ pos += (l->pitch - w * 2);
+ } else {
+ memset(pos, col, w);
+ pos += l->pitch;
+ }
+ }
+ addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH);
+}
+
+uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) {
+ if (layer < 0 || layer > 1)
+ return 0;
+
+ TownsScreenLayer *l = &_layers[layer];
+ if (!l->ready)
+ return 0;
+
+ return l->pixels + y * l->pitch + x * l->bpp;
+}
+
+int TownsScreen::getLayerPitch(int layer) {
+ if (layer >= 0 && layer < 2)
+ return _layers[layer].pitch;
+ return 0;
+}
+
+int TownsScreen::getLayerHeight(int layer) {
+ if (layer >= 0 && layer < 2)
+ return _layers[layer].height;
+ return 0;
+}
+
+int TownsScreen::getLayerBpp(int layer) {
+ if (layer >= 0 && layer < 2)
+ return _layers[layer].bpp;
+ return 0;
+}
+
+int TownsScreen::getLayerScaleW(int layer) {
+ if (layer >= 0 && layer < 2)
+ return _layers[layer].scaleW;
+ return 0;
+}
+
+int TownsScreen::getLayerScaleH(int layer) {
+ if (layer >= 0 && layer < 2)
+ return _layers[layer].scaleH;
+ return 0;
+}
+
+void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
+ if (w <= 0 || h <= 0 || _numDirtyRects > DIRTY_RECTS_MAX)
+ return;
+
+ if (_numDirtyRects == DIRTY_RECTS_MAX) {
+ // full redraw
+ _dirtyRects.clear();
+ _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+ _numDirtyRects++;
+ return;
+ }
+
+ int x2 = x + w - 1;
+ int y2 = y + h - 1;
+
+ assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height);
+
+ bool skip = false;
+ for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) {
+ // Try to merge new rect with an existing rect (only once, since trying to merge
+ // more than one overlapping rect would be causing more overhead than doing any good).
+ if (x > r->left && x < r->right && y > r->top && y < r->bottom) {
+ x = r->left;
+ y = r->top;
+ skip = true;
+ }
+
+ if (x2 > r->left && x2 < r->right && y > r->top && y < r->bottom) {
+ x2 = r->right;
+ y = r->top;
+ skip = true;
+ }
+
+ if (x2 > r->left && x2 < r->right && y2 > r->top && y2 < r->bottom) {
+ x2 = r->right;
+ y2 = r->bottom;
+ skip = true;
+ }
+
+ if (x > r->left && x < r->right && y2 > r->top && y2 < r->bottom) {
+ x = r->left;
+ y2 = r->bottom;
+ skip = true;
+ }
+
+ if (skip) {
+ r->left = x;
+ r->top = y;
+ r->right = x2;
+ r->bottom = y2;
+ break;
+ }
+ }
+
+ if (!skip) {
+ _dirtyRects.push_back(Common::Rect(x, y, x2, y2));
+ _numDirtyRects++;
+ }
+}
+
+void TownsScreen::toggleLayers(int flag) {
+ if (flag < 0 || flag > 3)
+ return;
+
+ for (int i = 0; i < 2; ++i) {
+ _layers[i].enabled = (flag & (i + 1)) ? true : false;
+ _layers[i].onBottom = (!i || !_layers[0].enabled);
+ }
+
+ _dirtyRects.clear();
+ _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
+ _numDirtyRects = FULL_REDRAW;
+
+ memset(_outBuffer, 0, _pitch * _height);
+ updateOutputBuffer();
+ outputToScreen();
+
+ _system->updateScreen();
+}
+
+void TownsScreen::update() {
+ updateOutputBuffer();
+ outputToScreen();
+}
+
+void TownsScreen::updateOutputBuffer() {
+ for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) {
+ for (int i = 0; i < 2; i++) {
+
+ TownsScreenLayer *l = &_layers[i];
+ if (!l->enabled || !l->ready)
+ continue;
+
+ uint8 *dst = _outBuffer + r->top * _pitch + r->left * _bpp;
+ int ptch = _pitch - (r->right - r->left + 1) * _bpp;
+
+ if (_bpp == 2 && l->bpp == 1) {
+ for (int ic = 0; ic < l->numCol; ic++)
+ l->bltTmpPal[ic] = calc16BitColor(&l->palette[ic * 3]);
+ }
+
+ for (int y = r->top; y <= r->bottom; ++y) {
+ if (l->bpp == _bpp && l->scaleW == 1 && l->onBottom) {
+ memcpy(dst, l->bltInternY[y] + l->bltInternX[r->left], (r->right + 1 - r->left) * _bpp);
+ dst += _pitch;
+
+ } else if (_bpp == 2) {
+ for (int x = r->left; x <= r->right; ++x) {
+ uint8 *src = l->bltInternY[y] + l->bltInternX[x];
+ if (l->bpp == 1) {
+ uint8 col = *src;
+ if (col || l->onBottom) {
+ if (l->numCol == 16)
+ col = (col >> 4) & (col & 0x0f);
+ *(uint16*)dst = l->bltTmpPal[col];
+ }
+ } else {
+ *(uint16*)dst = *(uint16*)src;
+ }
+ dst += 2;
+ }
+ dst += ptch;
+
+ } else {
+ for (int x = r->left; x <= r->right; ++x) {
+ uint8 col = *(l->bltInternY[y] + l->bltInternX[x]);
+ if (col || l->onBottom) {
+ if (l->numCol == 16)
+ col = (col >> 4) & (col & 0x0f);
+ *dst = col;
+ }
+ dst++;
+ }
+ dst += ptch;
+ }
+ }
+ }
+ }
+}
+
+void TownsScreen::outputToScreen() {
+ for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i)
+ _system->copyRectToScreen(_outBuffer + i->top * _pitch + i->left * _bpp, _pitch, i->left, i->top, i->right - i->left + 1, i->bottom - i->top + 1);
+ _dirtyRects.clear();
+ _numDirtyRects = 0;
+}
+
+uint16 TownsScreen::calc16BitColor(const uint8 *palEntry) {
+ uint16 ar = (palEntry[0] & 0xf8) << 7;
+ uint16 ag = (palEntry[1] & 0xf8) << 2;
+ uint16 ab = (palEntry[2] >> 3);
+ uint16 col = ar | ag | ab;
+ return col;
+}
+
+#undef DIRTY_RECTS_MAX
+#undef FULL_REDRAW
+
+} // End of namespace Scumm
+
+#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index 1ac7e98689..361a3bc165 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -2088,7 +2088,7 @@ void Wiz::displayWizComplexImage(const WizParameters *params) {
if (_vm->_fullRedraw && dstResNum == 0) {
if (sourceImage != 0 || (params->processFlags & (kWPFScaled | kWPFRotate)))
- error("Can't do this command in the enter script.");
+ error("Can't do this command in the enter script");
assert(_imagesNum < ARRAYSIZE(_images));
WizImage *pwi = &_images[_imagesNum];
diff --git a/engines/scumm/help.cpp b/engines/scumm/help.cpp
index d9bdf51d19..e15c4a5592 100644
--- a/engines/scumm/help.cpp
+++ b/engines/scumm/help.cpp
@@ -254,7 +254,7 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo
case 4:
title = "Other game controls:";
if (version <= 2) {
- ADD_TEXT("Inventory: (not yet implemented)");
+ ADD_TEXT("Inventory:");
ADD_BIND("u", "Scroll list up");
ADD_BIND("j", "Scroll list down");
ADD_BIND("i", "Upper left item");
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 73aec472e4..a90915e438 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -47,7 +47,6 @@ namespace Scumm {
#define PERCUSSION_CHANNEL 9
extern MidiParser *MidiParser_createRO();
-extern MidiParser *MidiParser_createEUP();
uint16 Player::_active_notes[128];
@@ -193,9 +192,6 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
if (!memcmp(ptr, "RO", 2)) {
// Old style 'RO' resource
_parser = MidiParser_createRO();
- } else if (!memcmp(ptr, "SO", 2)) {
- // Euphony (FM-TOWNS) resource
- _parser = MidiParser_createEUP();
} else if (!memcmp(ptr, "FORM", 4)) {
// Humongous Games XMIDI resource
_parser = MidiParser::createParser_XMIDI();
diff --git a/engines/scumm/midiparser_eup.cpp b/engines/scumm/midiparser_eup.cpp
deleted file mode 100644
index 592d43f7fe..0000000000
--- a/engines/scumm/midiparser_eup.cpp
+++ /dev/null
@@ -1,222 +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 "sound/midiparser.h"
-#include "sound/mididrv.h"
-#include "common/util.h"
-
-namespace Scumm {
-
-/**
- * The FM-TOWNS Euphony version of MidiParser.
- */
-class MidiParser_EUP : public MidiParser {
-protected:
- byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel #
- byte *_instr_to_channel;
- struct {
- byte *enable;
- int8 *channel;
- int8 *volume;
- int8 *transpose;
- } _presets;
- bool _loop;
- byte _presend; // Tracks which startup implied events have been sent.
- uint32 _base_tick; // Events times are relative to this base.
-
-protected:
- void parseNextEvent (EventInfo &info);
- void resetTracking();
-
-public:
- bool loadMusic (byte *data, uint32 size);
-};
-
-
-
-//////////////////////////////////////////////////
-//
-// MidiParser_EUP implementation
-//
-//////////////////////////////////////////////////
-
-void MidiParser_EUP::parseNextEvent (EventInfo &info) {
- byte *pos = _position._play_pos;
-
- // FIXME: The presend is for sending init events
- // that aren't actually in the stream. This would
- // be for, e.g., instrument setup. Right now, we
- // don't actually use the instruments specified
- // in the music header. We're sending fixed GM
- // program changes to get a reasonable "one-size-
- // fits-all" sound until we actually support the
- // FM synthesis capabilities of FM-TOWNS.
- for (; _presend < 12; ++_presend) {
- if (_instr_to_channel[_presend >> 1] >= 16)
- continue;
- info.start = pos;
- info.delta = 0;
- if (_presend & 1) {
- byte *data = &_instruments[_presend >> 1][0];
- data[1] = _instr_to_channel[_presend >> 1];
- info.event = 0xF0;
- info.ext.data = data;
- info.length = 48;
- } else {
- info.event = 0xB0 | (_presend >> 1);
- info.basic.param1 = 121;
- info.basic.param2 = 0;
- }
- ++_presend;
- return;
- }
-
- while (true) {
- byte cmd = *pos;
- if ((cmd & 0xF0) == 0x90) {
- byte preset = pos[1];
- byte channel = _presets.channel[preset];
- if (channel >= 16)
- channel = cmd & 0x0F;
- uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick;
- int note = (int) pos[4] + _presets.transpose[preset];
- int volume = (int) pos[5];
- // HACK: Loom-Towns distaff tracks seem to
- // contain zero-volume note events, so change
- // those to full volume.
- if (!volume)
- volume = 127;
- volume += _presets.volume[preset];
- if (volume > 127)
- volume = 127;
- else if (volume < 0)
- volume = 0;
- pos += 6;
- if (_presets.enable[preset]) {
- uint16 duration = pos[1] | (pos[2] << 4);
- info.start = pos;
- uint32 last = _position._last_event_tick;
- info.delta = (tick < last) ? 0 : (tick - last);
- info.event = 0x90 | channel;
- info.length = duration;
- info.basic.param1 = note;
- info.basic.param2 = volume;
- pos += 6;
- break;
- }
- pos += 6;
- } else if (cmd == 0xF2) {
- // This is a "measure marker" of sorts.
- // It advances the "base time", to which
- // all event times are relative.
- _base_tick += (pos[3] << 7) | pos[2];
- pos += 6;
- } else if (cmd == 0xF8) {
- // TODO: Implement this.
- pos += 6;
- } else if (cmd == 0xFD || cmd == 0xFE) {
- // End of track.
- if (_loop && false) {
- // TODO: Implement this.
- } else {
- info.start = pos;
- uint32 last = _position._last_event_tick;
- info.delta = (_base_tick < last) ? 0 : (_base_tick - last);
- info.event = 0xFF;
- info.length = 0;
- info.ext.type = 0x2F;
- info.ext.data = pos;
- break;
- }
- } else {
- error("Unknown Euphony music event 0x%02X", (int) cmd);
- memset(&info, 0, sizeof(info));
- pos = 0;
- break;
- }
- }
- _position._play_pos = pos;
-}
-
-bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
- unloadMusic();
- byte *pos = data;
- int i;
-
- if (memcmp(pos, "SO", 2)) {
- error("'SO' header expected but found '%c%c' instead.", pos[0], pos[1]);
- return false;
- }
-
- byte numInstruments = pos[16];
- pos += 16 + 2;
- for (i = 0; i < numInstruments; ++i) {
- _instruments[i][0] = 0x7C;
- memcpy (&_instruments[i][2], pos, 48);
- pos += 48;
- }
-
- // Load the prest pointers
- _presets.enable = pos;
- pos += 32;
- _presets.channel = (int8 *) pos;
- pos += 32;
- _presets.volume = (int8 *) pos;
- pos += 32;
- _presets.transpose = (int8 *) pos;
- pos += 32;
-
- pos += 8; // Unknown bytes
- _instr_to_channel = pos; // Instrument-to-channel mapping
- pos += 6;
- pos += 4; // Skip the music size for now.
- pos++; // Unknown byte
- byte tempo = *pos++;
- _loop = (*pos++ != 1);
- pos++; // Unknown byte
-
- _num_tracks = 1;
- _ppqn = 120;
- _tracks[0] = pos;
-
- // Note that we assume the original data passed in
- // will persist beyond this call, i.e. we do NOT
- // copy the data to our own buffer. Take warning....
- resetTracking();
- setTempo (1000000 * 60 / tempo);
- setTrack (0);
- return true;
-}
-
-void MidiParser_EUP::resetTracking() {
- MidiParser::resetTracking();
- _presend = 0;
- _base_tick = 0;
-}
-
-MidiParser *MidiParser_createEUP() { return new MidiParser_EUP; }
-
-} // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index e5f0745dd6..cd89f5ffad 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -16,6 +16,7 @@ MODULE_OBJS := \
dialogs.o \
file.o \
file_nes.o \
+ gfx_towns.o \
gfx.o \
he/resource_he.o \
he/script_v60he.o \
@@ -29,7 +30,6 @@ MODULE_OBJS := \
imuse/sysex_samnmax.o \
imuse/sysex_scumm.o \
input.o \
- midiparser_eup.o \
midiparser_ro.o \
object.o \
palette.o \
@@ -37,6 +37,7 @@ MODULE_OBJS := \
player_nes.o \
player_pce.o \
player_sid.o \
+ player_towns.o \
player_v1.o \
player_v2.o \
player_v2a.o \
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index c6ac53b862..e2b68f8d3b 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -714,7 +714,7 @@ void ScummEngine_v70he::storeFlObject(int slot) {
memcpy(&_storedFlObjects[_numStoredFlObjects], &_objs[slot], sizeof(_objs[slot]));
_numStoredFlObjects++;
if (_numStoredFlObjects > 100)
- error("Too many flobjects saved on room transition.");
+ error("Too many flobjects saved on room transition");
}
void ScummEngine_v70he::restoreFlObjects() {
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 09da1b47c4..5c0c58595b 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -30,6 +30,7 @@
#include "scumm/scumm_v6.h"
#include "scumm/scumm_v8.h"
#include "scumm/util.h"
+#include "scumm/charset.h"
namespace Scumm {
@@ -56,11 +57,17 @@ uint16 ScummEngine::get16BitColor(uint8 r, uint8 g, uint8 b) {
void ScummEngine::resetPalette() {
static const byte tableC64Palette[] = {
+#if 1 // VICE-based palette. See bug #2847001
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x7E, 0x35, 0x2B, 0x6E, 0xB7, 0xC1,
+ 0x7F, 0x3B, 0xA6, 0x5C, 0xA0, 0x35, 0x33, 0x27, 0x99, 0xCB, 0xD7, 0x65,
+ 0x85, 0x53, 0x1C, 0x50, 0x3C, 0x00, 0xB4, 0x6B, 0x61, 0x4A, 0x4A, 0x4A,
+ 0x75, 0x75, 0x75, 0xA3, 0xE7, 0x7C, 0x70, 0x64, 0xD6, 0xA3, 0xA3, 0xA3,
+#else
0x00, 0x00, 0x00, 0xFD, 0xFE, 0xFC, 0xBE, 0x1A, 0x24, 0x30, 0xE6, 0xC6,
0xB4, 0x1A, 0xE2, 0x1F, 0xD2, 0x1E, 0x21, 0x1B, 0xAE, 0xDF, 0xF6, 0x0A,
0xB8, 0x41, 0x04, 0x6A, 0x33, 0x04, 0xFE, 0x4A, 0x57, 0x42, 0x45, 0x40,
0x70, 0x74, 0x6F, 0x59, 0xFE, 0x59, 0x5F, 0x53, 0xFE, 0xA4, 0xA7, 0xA2,
-
+#endif
// Use 17 color table for v1 games to allow correct color for inventory and
// sentence line. Original games used some kind of dynamic color table
// remapping between rooms.
@@ -133,6 +140,24 @@ void ScummEngine::resetPalette() {
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00
};
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ static const byte tableTownsV3Palette[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x00, 0xA0, 0xA0,
+ 0xA0, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0xA0, 0x60, 0x00, 0xA0, 0xA0, 0xA0,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0xE0,
+ 0xE0, 0x80, 0x80, 0xE0, 0x00, 0xE0, 0xE0, 0xE0, 0x00, 0xE0, 0xE0, 0xE0
+ };
+
+ static const byte tableTownsLoomPalette[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0x00, 0xAB, 0xAB,
+ 0xAB, 0x00, 0x00, 0x69, 0x29, 0x45, 0x8C, 0x4D, 0x14, 0xAB, 0xAB, 0xAB,
+ 0x57, 0x3F, 0x57, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0x57, 0x57, 0xFF, 0xFF,
+ 0xFF, 0x57, 0x57, 0xD6, 0x94, 0x40, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0xFF
+ };
+#endif
+#endif
+
if (_game.version <= 1) {
if (_game.platform == Common::kPlatformApple2GS) {
// TODO: unique palette?
@@ -192,6 +217,19 @@ void ScummEngine::resetPalette() {
// else we initialise and then lock down the first 16 colors.
if (_renderMode != Common::kRenderEGA)
setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ } else if (_game.platform == Common::kPlatformFMTowns) {
+ if (_game.id == GID_INDY4 || _game.id == GID_MONKEY2)
+ _townsClearLayerFlag = 0;
+#ifdef USE_RGB_COLOR
+ else if (_game.id == GID_LOOM)
+ towns_setTextPaletteFromPtr(tableTownsLoomPalette);
+ else if (_game.version == 3)
+ towns_setTextPaletteFromPtr(tableTownsV3Palette);
+#endif
+
+ _townsScreen->toggleLayers(_townsActiveLayerFlags);
+#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
}
setDirtyColors(0, 255);
}
@@ -459,6 +497,11 @@ void ScummEngine::cyclePalette() {
int valueToAdd;
int i, j;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+ return;
+#endif
+
valueToAdd = VAR(VAR_TIMER);
if (valueToAdd < VAR(VAR_TIMER_NEXT))
valueToAdd = VAR(VAR_TIMER_NEXT);
@@ -500,6 +543,11 @@ void ScummEngine::moveMemInPalRes(int start, int end, byte direction) {
}
void ScummEngine::palManipulateInit(int resID, int start, int end, int time) {
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1))
+ return;
+#endif
+
byte *string1 = getStringAddress(resID);
byte *string2 = getStringAddress(resID + 1);
byte *string3 = getStringAddress(resID + 2);
@@ -967,6 +1015,12 @@ void ScummEngine::setCurrentPalette(int palindex) {
pals = getPalettePtr(_curPalIndex, _roomResource);
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
setPCEPaletteFromPtr(pals);
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ } else if (_game.platform == Common::kPlatformFMTowns) {
+ towns_setPaletteFromPtr(pals);
+#endif
+#endif
} else {
setPaletteFromPtr(pals);
}
@@ -1063,10 +1117,23 @@ void ScummEngine::updatePalette() {
}
}
- _system->setPalette(palette_colors, first, num);
-
_palDirtyMax = -1;
_palDirtyMin = 256;
+
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ p = palette_colors;
+ for (i = first; i < first + num; ++i) {
+ _16BitPalette[i] = get16BitColor(p[0], p[1], p[2]);
+ p += 4;
+ }
+ return;
+ }
+#endif
+#endif
+
+ _system->setPalette(palette_colors, first, num);
}
} // End of namespace Scumm
diff --git a/engines/scumm/player_sid.cpp b/engines/scumm/player_sid.cpp
index 3f7b65cbb4..35671e094b 100644
--- a/engines/scumm/player_sid.cpp
+++ b/engines/scumm/player_sid.cpp
@@ -92,8 +92,8 @@ static const uint16 FREQ_TBL[97] = {
static const int SONG_CHANNEL_OFFSET[3] = { 6, 8, 10 };
static const int RES_ID_CHANNEL[3] = { 3, 4, 5 };
-#define LOBYTE(a) ((a) & 0xFF)
-#define HIBYTE(a) (((a) >> 8) & 0xFF)
+#define LOBYTE_(a) ((a) & 0xFF)
+#define HIBYTE_(a) (((a) >> 8) & 0xFF)
#define GETBIT(var, pos) ((var) & (1<<(pos)))
@@ -176,14 +176,14 @@ void Player_SID::handleMusicBuffer() { // $33cd
l_chanBuf[19] = phaseBit[channel];
l_chanBuf[10] |= 0x01; // attack phase
}
- l_chanBuf[11] = LOBYTE(l_freq);
- l_chanBuf[12] = HIBYTE(l_freq);
+ l_chanBuf[11] = LOBYTE_(l_freq);
+ l_chanBuf[12] = HIBYTE_(l_freq);
releasePhase[channel] = false;
}
// set counter value for frequency update (freqDeltaCounter)
- l_chanBuf[13] = LOBYTE(curStepSum);
- l_chanBuf[14] = HIBYTE(curStepSum);
+ l_chanBuf[13] = LOBYTE_(curStepSum);
+ l_chanBuf[14] = HIBYTE_(curStepSum);
_soundQueue[channel] = RES_ID_CHANNEL[channel];
processSongData(channel);
@@ -340,8 +340,8 @@ void Player_SID::processSongData(int channel) { // $4939
if (songFileOrChanBufData == NULL) { // chanBuf (4C1C)
/*
// TODO: do we need this?
- LOBYTE(vec20[channel]) = 0;
- LOBYTE(songPosPtr[channel]) = LOBYTE(songFileOrChanBufOffset[channel]);
+ LOBYTE_(vec20[channel]) = 0;
+ LOBYTE_(songPosPtr[channel]) = LOBYTE_(songFileOrChanBufOffset[channel]);
*/
releaseResourceUnk(channel);
return;
@@ -551,8 +551,8 @@ void Player_SID::setSIDFreqAS(int channel) { // $4be6
if (swapVarLoaded)
return;
int reg = SID_REG_OFFSET[channel];
- SID_Write(reg, LOBYTE(freqReg[channel])); // freq/pulseWidth voice 1/2/3
- SID_Write(reg+1, HIBYTE(freqReg[channel]));
+ SID_Write(reg, LOBYTE_(freqReg[channel])); // freq/pulseWidth voice 1/2/3
+ SID_Write(reg+1, HIBYTE_(freqReg[channel]));
if (channel < 3) {
SID_Write(reg+5, attackReg[channel]); // attack
SID_Write(reg+6, sustainReg[channel]); // sustain
@@ -839,8 +839,8 @@ void Player_SID::useSwapVars(int channel) { // $5342
SID_Write(24, SIDReg24);
// filter freq.
- SID_Write(21, LOBYTE(freqReg[3]));
- SID_Write(22, HIBYTE(freqReg[3]));
+ SID_Write(21, LOBYTE_(freqReg[3]));
+ SID_Write(22, HIBYTE_(freqReg[3]));
} else {
SIDReg23 = SIDReg23Stuff & BITMASK_INV[channel];
SID_Write(23, SIDReg23);
diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp
new file mode 100644
index 0000000000..06f97fd671
--- /dev/null
+++ b/engines/scumm/player_towns.cpp
@@ -0,0 +1,748 @@
+/* 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 "scumm/sound.h"
+#include "scumm/player_towns.h"
+
+namespace Scumm {
+
+Player_Towns::Player_Towns(ScummEngine *vm, bool isVersion2) : _vm(vm), _v2(isVersion2), _numSoundMax(isVersion2 ? 256 : 200) {
+ memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound));
+ _unkFlags = 0x33;
+ _intf = 0;
+}
+
+void Player_Towns::setSfxVolume(int vol) {
+ if (!_intf)
+ return;
+ _intf->setSoundEffectVolume(vol);
+}
+
+int Player_Towns::getSoundStatus(int sound) const {
+ if (!_intf)
+ return 0;
+ for (int i = 1; i < 9; i++) {
+ if (_pcmCurrentSound[i].index == sound)
+ return _intf->callback(40, 0x3f + i) ? 1 : 0;
+ }
+ return 0;
+}
+
+void Player_Towns::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry pcmEntries[] = {
+ MKLINE(PcmCurrentSound, index, sleInt16, VER(81)),
+ MKLINE(PcmCurrentSound, chan, sleInt16, VER(81)),
+ MKLINE(PcmCurrentSound, note, sleUint8, VER(81)),
+ MKLINE(PcmCurrentSound, velo, sleUint8, VER(81)),
+ MKLINE(PcmCurrentSound, pan, sleUint8, VER(81)),
+ MKLINE(PcmCurrentSound, paused, sleUint8, VER(81)),
+ MKLINE(PcmCurrentSound, looping, sleUint8, VER(81)),
+ MKLINE(PcmCurrentSound, priority, sleUint32, VER(81)),
+ MKEND()
+ };
+
+ for (int i = 1; i < 9; i++) {
+ if (!_pcmCurrentSound[i].index)
+ continue;
+
+ if (_intf->callback(40, i + 0x3f))
+ continue;
+
+ _intf->callback(39, i + 0x3f);
+
+ _pcmCurrentSound[i].index = 0;
+ }
+
+ ser->saveLoadArrayOf(_pcmCurrentSound, 9, sizeof(PcmCurrentSound), pcmEntries);
+}
+
+void Player_Towns::restoreAfterLoad() {
+ for (int i = 1; i < 9; i++) {
+ if (!_pcmCurrentSound[i].index || _pcmCurrentSound[i].index == 0xffff)
+ continue;
+
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
+ if (!ptr)
+ continue;
+
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ if (ptr[13])
+ continue;
+
+ playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note, _pcmCurrentSound[i].priority);
+ }
+}
+
+void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note, int priority) {
+ if (!_intf)
+ return;
+
+ const uint8 *sfxData = data + 16;
+
+ int numChan = _v2 ? 1 : data[14];
+ for (int i = 0; i < numChan; i++) {
+ int chan = allocatePcmChannel(sound, i, priority);
+ if (!chan)
+ return;
+
+ _intf->callback(70, _unkFlags);
+ _intf->callback(3, chan + 0x3f, pan);
+ _intf->callback(37, chan + 0x3f, note, velo, sfxData);
+
+ _pcmCurrentSound[chan].note = note;
+ _pcmCurrentSound[chan].velo = velo;
+ _pcmCurrentSound[chan].pan = pan;
+ _pcmCurrentSound[chan].paused = 0;
+ _pcmCurrentSound[chan].looping = READ_LE_UINT32(&sfxData[20]) ? 1 : 0;
+
+ sfxData += (READ_LE_UINT32(&sfxData[12]) + 32);
+ }
+}
+
+void Player_Towns::stopPcmTrack(int sound) {
+ if (!_intf)
+ return;
+
+ for (int i = 1; i < 9; i++) {
+ if (sound == _pcmCurrentSound[i].index || !sound) {
+ _intf->callback(39, i + 0x3f);
+ _pcmCurrentSound[i].index = 0;
+ }
+ }
+}
+
+int Player_Towns::allocatePcmChannel(int sound, int sfxChanRelIndex, uint32 priority) {
+ if (!_intf)
+ return 0;
+
+ int chan = 0;
+
+ if (_v2 && priority > 255) {
+ chan = 8;
+ if (_intf->callback(40, 0x47))
+ _intf->callback(39, 0x47);
+ } else {
+ for (int i = 8; i; i--) {
+ if (!_pcmCurrentSound[i].index) {
+ chan = i;
+ continue;
+ }
+
+ if (_intf->callback(40, i + 0x3f))
+ continue;
+
+ chan = i;
+ if (_pcmCurrentSound[chan].index == 0xffff)
+ _intf->callback(39, chan + 0x3f);
+ else
+ _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
+ }
+
+ if (!chan) {
+ for (int i = 1; i < 9; i++) {
+ if (priority >= _pcmCurrentSound[i].priority)
+ chan = i;
+ }
+ if (_pcmCurrentSound[chan].index == 0xffff)
+ _intf->callback(39, chan + 0x3f);
+ else
+ _vm->_sound->stopSound(_pcmCurrentSound[chan].index);
+ }
+ }
+
+ if (chan) {
+ _pcmCurrentSound[chan].index = sound;
+ _pcmCurrentSound[chan].chan = sfxChanRelIndex;
+ _pcmCurrentSound[chan].priority = priority;
+ }
+
+ return chan;
+}
+
+Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_Towns(vm, false) {
+ _soundOverride = 0;
+ _cdaCurrentSound = _eupCurrentSound = _cdaNumLoops = 0;
+ _cdaForceRestart = 0;
+ _cdaVolLeft = _cdaVolRight = 0;
+
+ _eupVolLeft = _eupVolRight = 0;
+ _eupLooping = false;
+
+ if (_vm->_game.version == 3) {
+ _soundOverride = new SoundOvrParameters[_numSoundMax];
+ memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
+ }
+
+ _driver = new TownsEuphonyDriver(mixer);
+}
+
+Player_Towns_v1::~Player_Towns_v1() {
+ delete _driver;
+ delete[] _soundOverride;
+}
+
+bool Player_Towns_v1::init() {
+ if (!_driver)
+ return false;
+
+ if (!_driver->init())
+ return false;
+
+ _driver->reserveSoundEffectChannels(8);
+ _intf = _driver->intf();
+
+ // Treat all 6 fm channels and all 8 pcm channels as sound effect channels
+ // since music seems to exist as CD audio only in the games which use this
+ // MusicEngine implementation.
+ _intf->setSoundEffectChanMask(-1);
+
+ setVolumeCD(255, 255);
+
+ return true;
+}
+
+void Player_Towns_v1::setMusicVolume(int vol) {
+ _driver->setMusicVolume(vol);
+}
+
+void Player_Towns_v1::startSound(int sound) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ int type = ptr[13];
+
+ if (type == 0) {
+ uint8 velocity = 0;
+ uint8 note = 0;
+
+ if (_vm->_game.version == 3) {
+ velocity = (_soundOverride[sound].vLeft + _soundOverride[sound].vRight);
+ note = _soundOverride[sound].note;
+ }
+
+ velocity = velocity ? velocity >> 2 : ptr[14] >> 1;
+ playPcmTrack(sound, ptr + 6, velocity, 64, note ? note : ptr[50], READ_LE_UINT16(ptr + 10));
+
+ } else if (type == 1) {
+ playEuphonyTrack(sound, ptr + 6);
+
+ } else if (type == 2) {
+ playCdaTrack(sound, ptr + 6);
+ }
+
+ if (_vm->_game.version == 3)
+ _soundOverride[sound].vLeft = _soundOverride[sound].vRight = _soundOverride[sound].note = 0;
+}
+
+void Player_Towns_v1::stopSound(int sound) {
+ if (sound == 0 || sound == _cdaCurrentSound) {
+ _cdaCurrentSound = 0;
+ _vm->_sound->stopCD();
+ _vm->_sound->stopCDTimer();
+ }
+
+ if (sound != 0 && sound == _eupCurrentSound) {
+ _eupCurrentSound = 0;
+ _eupLooping = false;
+ _driver->stopParser();
+ }
+
+ stopPcmTrack(sound);
+}
+
+void Player_Towns_v1::stopAllSounds() {
+ _cdaCurrentSound = 0;
+ _vm->_sound->stopCD();
+ _vm->_sound->stopCDTimer();
+
+ _eupCurrentSound = 0;
+ _eupLooping = false;
+ _driver->stopParser();
+
+ stopPcmTrack(0);
+}
+
+int Player_Towns_v1::getSoundStatus(int sound) const {
+ if (sound == _cdaCurrentSound)
+ return _vm->_sound->pollCD();
+ if (sound == _eupCurrentSound)
+ return _driver->parserIsPlaying() ? 1 : 0;
+ return Player_Towns::getSoundStatus(sound);
+}
+
+int32 Player_Towns_v1::doCommand(int numargs, int args[]) {
+ int32 res = 0;
+
+ switch (args[0]) {
+ case 2:
+ _driver->intf()->callback(73, 0);
+ break;
+
+ case 3:
+ restartLoopingSounds();
+ break;
+
+ case 8:
+ startSound(args[1]);
+ break;
+
+ case 9:
+ _vm->_sound->stopSound(args[1]);
+ break;
+
+ case 11:
+ stopPcmTrack(0);
+ break;
+
+ case 14:
+ startSoundEx(args[1], args[2], args[3], args[4]);
+ break;
+
+ case 15:
+ stopSoundSuspendLooping(args[1]);
+ break;
+
+ default:
+ warning("Player_Towns_v1::doCommand: Unknown command %d", args[0]);
+ break;
+ }
+
+ return res;
+}
+
+void Player_Towns_v1::setVolumeCD(int left, int right) {
+ _cdaVolLeft = left & 0xff;
+ _cdaVolRight = right & 0xff;
+ _driver->setOutputVolume(1, left >> 1, right >> 1);
+}
+
+void Player_Towns_v1::setSoundVolume(int sound, int left, int right) {
+ if (_soundOverride && sound > 0 && sound < _numSoundMax) {
+ _soundOverride[sound].vLeft = left;
+ _soundOverride[sound].vRight = right;
+ }
+}
+
+void Player_Towns_v1::setSoundNote(int sound, int note) {
+ if (_soundOverride && sound > 0 && sound < _numSoundMax)
+ _soundOverride[sound].note = note;
+}
+
+void Player_Towns_v1::saveLoadWithSerializer(Serializer *ser) {
+ _cdaCurrentSoundTemp = (_vm->_sound->pollCD() && _cdaNumLoops > 1) ? _cdaCurrentSound & 0xff : 0;
+ _cdaNumLoopsTemp = _cdaNumLoops & 0xff;
+
+ static const SaveLoadEntry cdEntries[] = {
+ MKLINE(Player_Towns_v1, _cdaCurrentSoundTemp, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _cdaNumLoopsTemp, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _cdaVolLeft, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _cdaVolRight, sleUint8, VER(81)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, cdEntries);
+
+ if (!_eupLooping && !_driver->parserIsPlaying())
+ _eupCurrentSound = 0;
+
+ static const SaveLoadEntry eupEntries[] = {
+ MKLINE(Player_Towns_v1, _eupCurrentSound, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _eupLooping, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _eupVolLeft, sleUint8, VER(81)),
+ MKLINE(Player_Towns_v1, _eupVolRight, sleUint8, VER(81)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, eupEntries);
+
+ Player_Towns::saveLoadWithSerializer(ser);
+}
+
+void Player_Towns_v1::restoreAfterLoad() {
+ setVolumeCD(_cdaVolLeft, _cdaVolRight);
+
+ if (_cdaCurrentSoundTemp) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _cdaCurrentSoundTemp) + 6;
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ if (ptr[7] == 2) {
+ playCdaTrack(_cdaCurrentSoundTemp, ptr, true);
+ _cdaCurrentSound = _cdaCurrentSoundTemp;
+ _cdaNumLoops = _cdaNumLoopsTemp;
+ }
+ }
+
+ if (_eupCurrentSound) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _eupCurrentSound) + 6;
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ if (ptr[7] == 1) {
+ setSoundVolume(_eupCurrentSound, _eupVolLeft, _eupVolRight);
+ playEuphonyTrack(_eupCurrentSound, ptr);
+ }
+ }
+
+ Player_Towns::restoreAfterLoad();
+}
+
+void Player_Towns_v1::restartLoopingSounds() {
+ if (_cdaNumLoops && !_cdaForceRestart)
+ _cdaForceRestart = 1;
+
+ for (int i = 1; i < 9; i++) {
+ if (!_pcmCurrentSound[i].paused)
+ continue;
+
+ _pcmCurrentSound[i].paused = 0;
+
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
+ if (!ptr)
+ continue;
+ ptr += 24;
+
+ int c = 1;
+ while (_pcmCurrentSound[i].chan != c) {
+ ptr = ptr + READ_LE_UINT32(&ptr[12]) + 32;
+ c++;
+ }
+
+ _driver->playSoundEffect(i + 0x3f, _pcmCurrentSound[i].note, _pcmCurrentSound[i].velo, ptr);
+ }
+
+ _driver->intf()->callback(73, 1);
+}
+
+void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2;
+
+ if (pan > 99)
+ pan = 99;
+
+ velo = velo ? (velo * ptr[14] + 50) / 100 : ptr[14];
+ velo = CLIP(velo, 1, 255);
+ uint16 pri = READ_LE_UINT16(ptr + 10);
+
+ if (ptr[13] == 0) {
+ velo >>= 1;
+
+ if (!velo)
+ velo = 1;
+
+ pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64;
+
+ playPcmTrack(sound, ptr + 6, velo ? velo : ptr[14] >> 1, pan, note ? note : ptr[50], pri);
+
+ } else if (ptr[13] == 2) {
+ int volLeft = velo;
+ int volRight = velo;
+
+ if (pan < 50)
+ volRight = ((pan * 2 + 1) * velo + 50) / 100;
+ else if (pan > 50)
+ volLeft = (((99 - pan) * 2 + 1) * velo + 50) / 100;
+
+ setVolumeCD(volLeft, volRight);
+
+ if (!_cdaForceRestart && sound == _cdaCurrentSound)
+ return;
+
+ playCdaTrack(sound, ptr + 6, true);
+ }
+}
+
+void Player_Towns_v1::stopSoundSuspendLooping(int sound) {
+ if (!sound) {
+ return;
+ } else if (sound == _cdaCurrentSound) {
+ if (_cdaNumLoops && _cdaForceRestart)
+ _cdaForceRestart = 1;
+ } else {
+ for (int i = 1; i < 9; i++) {
+ if (sound == _pcmCurrentSound[i].index) {
+ if (!_driver->soundEffectIsPlaying(i + 0x3f))
+ continue;
+ _driver->stopSoundEffect(i + 0x3f);
+ if (_pcmCurrentSound[i].looping)
+ _pcmCurrentSound[i].paused = 1;
+ else
+ _pcmCurrentSound[i].index = 0;
+ }
+ }
+ }
+}
+
+void Player_Towns_v1::playEuphonyTrack(int sound, const uint8 *data) {
+ const uint8 *pos = data + 16;
+ const uint8 *src = pos + data[14] * 48;
+ const uint8 *trackData = src + 150;
+
+ for (int i = 0; i < 32; i++)
+ _driver->chanEnable(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanMode(i, 0xff);
+ 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 += 8;
+ for (int i = 0; i < 6; i++)
+ _driver->assignChannel(i, *src++);
+
+ for (int i = 0; i < data[14]; i++) {
+ _driver->loadInstrument(i, i, pos + i * 48);
+ _driver->intf()->callback(4, i, i);
+ }
+
+ _eupVolLeft = _soundOverride[sound].vLeft;
+ _eupVolRight = _soundOverride[sound].vRight;
+ int lvl = _soundOverride[sound].vLeft + _soundOverride[sound].vRight;
+ if (!lvl)
+ lvl = data[8] + data[9];
+ lvl >>= 2;
+
+ for (int i = 0; i < 6; i++)
+ _driver->chanVolume(i, lvl);
+
+ uint32 trackSize = READ_LE_UINT32(src);
+ src += 4;
+ uint8 startTick = *src++;
+
+ _driver->setMusicTempo(*src++);
+ _driver->startMusicTrack(trackData, trackSize, startTick);
+
+ _eupLooping = (*src != 1) ? 1 : 0;
+ _driver->setMusicLoop(_eupLooping != 0);
+ _driver->continueParsing();
+ _eupCurrentSound = sound;
+}
+
+void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) {
+ const uint8 *ptr = data;
+
+ if (!sound)
+ return;
+
+ if (!skipTrackVelo) {
+ if (_vm->_game.version == 3) {
+ if (_soundOverride[sound].vLeft + _soundOverride[sound].vRight)
+ setVolumeCD(_soundOverride[sound].vLeft, _soundOverride[sound].vRight);
+ else
+ setVolumeCD(ptr[8], ptr[9]);
+ } else {
+ setVolumeCD(ptr[8], ptr[9]);
+ }
+ }
+
+ if (sound == _cdaCurrentSound && _vm->_sound->pollCD() == 1)
+ return;
+
+ ptr += 16;
+
+ int track = ptr[0];
+ _cdaNumLoops = ptr[1];
+ int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
+
+ _vm->_sound->playCDTrack(track, _cdaNumLoops == 0xff ? -1 : _cdaNumLoops, start, end <= start ? 0 : end - start);
+ _cdaForceRestart = 0;
+ _cdaCurrentSound = sound;
+}
+
+Player_Towns_v2::Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse) : Player_Towns(vm, true), _imuse(imuse), _imuseDispose(disposeIMuse) {
+ _soundOverride = new SoundOvrParameters[_numSoundMax];
+ memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
+ _sblData = 0;
+ _intf = new TownsAudioInterface(mixer, 0);
+}
+
+Player_Towns_v2::~Player_Towns_v2() {
+ delete _intf;
+
+ if (_imuseDispose)
+ delete _imuse;
+
+ delete[] _sblData;
+ delete[] _soundOverride;
+}
+
+bool Player_Towns_v2::init() {
+ if (!_intf)
+ return false;
+
+ if (!_intf->init())
+ return false;
+
+ _intf->callback(33, 8);
+ _intf->setSoundEffectChanMask(~0x3f);
+
+ return true;
+}
+
+void Player_Towns_v2::setMusicVolume(int vol) {
+ _imuse->setMusicVolume(vol);
+}
+
+int Player_Towns_v2::getSoundStatus(int sound) const {
+ if (_soundOverride[sound].type == 7)
+ return Player_Towns::getSoundStatus(sound);
+ return _imuse->getSoundStatus(sound);
+}
+
+void Player_Towns_v2::startSound(int sound) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
+
+ if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) {
+ _soundOverride[sound].type = 7;
+ uint8 velo = _soundOverride[sound].velo ? _soundOverride[sound].velo - 1: (ptr[10] + ptr[11] + 1) >> 1;
+ uint8 pan = _soundOverride[sound].pan ? _soundOverride[sound].pan - 1 : 64;
+ uint8 pri = ptr[9];
+ _soundOverride[sound].velo = _soundOverride[sound].pan = 0;
+ playPcmTrack(sound, ptr + 8, velo, pan, ptr[52], pri);
+
+ } else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) {
+ _soundOverride[sound].type = 5;
+ playVocTrack(ptr + 27);
+
+ } else {
+ _soundOverride[sound].type = 3;
+ _imuse->startSound(sound);
+ }
+}
+
+void Player_Towns_v2::stopSound(int sound) {
+ if (_soundOverride[sound].type == 7) {
+ stopPcmTrack(sound);
+ } else {
+ _imuse->stopSound(sound);
+ }
+}
+
+void Player_Towns_v2::stopAllSounds() {
+ stopPcmTrack(0);
+ _imuse->stopAllSounds();
+}
+
+int32 Player_Towns_v2::doCommand(int numargs, int args[]) {
+ int32 res = -1;
+ uint8 *ptr = 0;
+
+ switch (args[0]) {
+ case 8:
+ startSound(args[1]);
+ res = 0;
+ break;
+
+ case 9:
+ case 15:
+ stopSound(args[1]);
+ res = 0;
+ break;
+
+ case 11:
+ stopPcmTrack(0);
+ break;
+
+ case 13:
+ res = getSoundStatus(args[1]);
+ break;
+
+ case 258:
+ if (_soundOverride[args[1]].type == 0) {
+ ptr = _vm->getResourceAddress(rtSound, args[1]);
+ if (READ_BE_UINT32(ptr) == MKID_BE('TOWS'))
+ _soundOverride[args[1]].type = 7;
+ }
+ if (_soundOverride[args[1]].type == 7) {
+ _soundOverride[args[1]].velo = args[2] + 1;
+ res = 0;
+ }
+ break;
+
+ case 259:
+ if (_soundOverride[args[1]].type == 0) {
+ ptr = _vm->getResourceAddress(rtSound, args[1]);
+ if (READ_BE_UINT32(ptr) == MKID_BE('TOWS'))
+ _soundOverride[args[1]].type = 7;
+ }
+ if (_soundOverride[args[1]].type == 7) {
+ _soundOverride[args[1]].pan = 64 - CLIP<int>(args[2], -63, 63);
+ res = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (res == -1)
+ return _imuse->doCommand(numargs, args);
+
+ return res;
+}
+
+void Player_Towns_v2::saveLoadWithSerializer(Serializer *ser) {
+ if (ser->getVersion() >= 83)
+ Player_Towns::saveLoadWithSerializer(ser);
+}
+
+void Player_Towns_v2::playVocTrack(const uint8 *data) {
+ static const uint8 header[] = {
+ 0x54, 0x61, 0x6C, 0x6B, 0x69, 0x65, 0x20, 0x20,
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x04, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00
+ };
+
+ uint32 len = (READ_LE_UINT32(data) >> 8) - 2;
+
+ int chan = allocatePcmChannel(0xffff, 0, 0x1000);
+ if (!chan)
+ return;
+
+ delete[] _sblData;
+ _sblData = new uint8[len + 32];
+
+ memcpy(_sblData, header, 32);
+ WRITE_LE_UINT32(_sblData + 12, len);
+
+ const uint8 *src = data + 6;
+ uint8 *dst = _sblData + 32;
+ for (uint32 i = 0; i < len; i++)
+ *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++;
+
+ _intf->callback(37, 0x3f + chan, 60, 127, _sblData);
+ _pcmCurrentSound[chan].paused = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_towns.h b/engines/scumm/player_towns.h
new file mode 100644
index 0000000000..e5023d25c2
--- /dev/null
+++ b/engines/scumm/player_towns.h
@@ -0,0 +1,181 @@
+/* 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 SCUMM_PLAYER_TOWNS_H
+#define SCUMM_PLAYER_TOWNS_H
+
+#include "scumm/scumm.h"
+#include "scumm/imuse/imuse.h"
+#include "sound/softsynth/fmtowns_pc98/towns_euphony.h"
+
+namespace Scumm {
+
+class Player_Towns : public MusicEngine {
+public:
+ Player_Towns(ScummEngine *vm, bool isVersion2);
+ virtual ~Player_Towns() {}
+
+ virtual bool init() = 0;
+
+ void setSfxVolume(int vol);
+
+ int getSoundStatus(int sound) const;
+
+ virtual int32 doCommand(int numargs, int args[]) = 0;
+
+ virtual void saveLoadWithSerializer(Serializer *ser);
+ virtual void restoreAfterLoad();
+
+ // version 1 specific
+ virtual int getCurrentCdaSound() { return 0; }
+ virtual int getCurrentCdaVolume() { return 0; }
+ virtual void setVolumeCD(int left, int right) {}
+ virtual void setSoundVolume(int sound, int left, int right) {}
+ virtual void setSoundNote(int sound, int note) {}
+
+protected:
+ void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0, int priority = 0);
+ void stopPcmTrack(int sound);
+
+ int allocatePcmChannel(int sound, int sfxChanRelIndex, uint32 priority);
+
+ struct PcmCurrentSound {
+ uint16 index;
+ uint16 chan;
+ uint8 note;
+ uint8 velo;
+ uint8 pan;
+ uint8 paused;
+ uint8 looping;
+ uint32 priority;
+ } _pcmCurrentSound[9];
+
+ uint8 _unkFlags;
+
+ TownsAudioInterface *_intf;
+ ScummEngine *_vm;
+
+ const int _numSoundMax;
+ const bool _v2;
+};
+
+class Player_Towns_v1 : public Player_Towns {
+public:
+ Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer);
+ ~Player_Towns_v1();
+
+ bool init();
+
+ void setMusicVolume(int vol);
+ void startSound(int sound);
+ void stopSound(int sound);
+ void stopAllSounds();
+
+ int getSoundStatus(int sound) const;
+ int getCurrentCdaSound() { return _cdaCurrentSound; }
+ int getCurrentCdaVolume() { return (_cdaVolLeft + _cdaVolRight + 1) >> 1; }
+
+ int32 doCommand(int numargs, int args[]);
+
+ void setVolumeCD(int left, int right);
+ void setSoundVolume(int sound, int left, int right);
+ void setSoundNote(int sound, int note);
+
+ void saveLoadWithSerializer(Serializer *ser);
+ void restoreAfterLoad();
+
+ TownsEuphonyDriver *driver() { return _driver; }
+
+private:
+ void restartLoopingSounds();
+ void startSoundEx(int sound, int velo, int pan, int note);
+ void stopSoundSuspendLooping(int sound);
+
+ void playEuphonyTrack(int sound, const uint8 *data);
+ void playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo = false);
+
+ struct SoundOvrParameters {
+ uint8 vLeft;
+ uint8 vRight;
+ uint8 note;
+ };
+
+ SoundOvrParameters *_soundOverride;
+
+ uint8 _cdaVolLeft;
+ uint8 _cdaVolRight;
+
+ uint8 _eupCurrentSound;
+ uint8 _eupLooping;
+ uint8 _eupVolLeft;
+ uint8 _eupVolRight;
+
+ uint8 _cdaCurrentSound;
+ uint8 _cdaNumLoops;
+ uint8 _cdaForceRestart;
+
+ uint8 _cdaCurrentSoundTemp;
+ uint8 _cdaNumLoopsTemp;
+
+ TownsEuphonyDriver *_driver;
+};
+
+class Player_Towns_v2 : public Player_Towns {
+public:
+ Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse);
+ ~Player_Towns_v2();
+
+ bool init();
+
+ void setMusicVolume(int vol);
+
+ int getSoundStatus(int sound) const;
+ void startSound(int sound);
+ void stopSound(int sound);
+ void stopAllSounds();
+
+ int32 doCommand(int numargs, int args[]);
+
+ void saveLoadWithSerializer(Serializer *ser);
+
+private:
+ void playVocTrack(const uint8 *data);
+
+ struct SoundOvrParameters {
+ uint8 velo;
+ uint8 pan;
+ uint8 type;
+ };
+
+ SoundOvrParameters *_soundOverride;
+
+ uint8 *_sblData;
+ IMuse *_imuse;
+ const bool _imuseDispose;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index e3e7bc1901..ba60a31061 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -28,6 +28,7 @@
#include "scumm/scumm.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
+#include "sound/softsynth/cms.h"
namespace Scumm {
@@ -40,395 +41,13 @@ namespace Scumm {
#define FB_WNOISE 0x12000 /* feedback for white noise */
#define FB_PNOISE 0x08000 /* feedback for periodic noise */
-// CMS/Gameblaster Emulation taken from DosBox
-
-#define LEFT 0x00
-#define RIGHT 0x01
-#define MAX_OUTPUT 0x7fff
-#define MIN_OUTPUT -0x8000
-//#define CMS_BUFFER_SIZE 128
-#define CMS_RATE 22050
-
#define PROCESS_ATTACK 1
#define PROCESS_RELEASE 2
#define PROCESS_SUSTAIN 3
#define PROCESS_DECAY 4
#define PROCESS_VIBRATO 5
-/* this structure defines a channel */
-struct saa1099_channel {
- int frequency; /* frequency (0x00..0xff) */
- int freq_enable; /* frequency enable */
- int noise_enable; /* noise enable */
- int octave; /* octave (0x00..0x07) */
- int amplitude[2]; /* amplitude (0x00..0x0f) */
- int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */
-
- /* vars to simulate the square wave */
- double counter;
- double freq;
- int level;
-};
-
-/* this structure defines a noise channel */
-struct saa1099_noise {
- /* vars to simulate the noise generator output */
- double counter;
- double freq;
- int level; /* noise polynomal shifter */
-};
-
-/* this structure defines a SAA1099 chip */
-struct SAA1099 {
- int stream; /* our stream */
- int noise_params[2]; /* noise generators parameters */
- int env_enable[2]; /* envelope generators enable */
- int env_reverse_right[2]; /* envelope reversed for right channel */
- int env_mode[2]; /* envelope generators mode */
- int env_bits[2]; /* non zero = 3 bits resolution */
- int env_clock[2]; /* envelope clock mode (non-zero external) */
- int env_step[2]; /* current envelope step */
- int all_ch_enable; /* all channels enable */
- int sync_state; /* sync all channels */
- int selected_reg; /* selected register */
- struct saa1099_channel channels[6]; /* channels */
- struct saa1099_noise noise[2]; /* noise generators */
-};
-
-static byte envelope[8][64] = {
- /* zero amplitude */
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- /* maximum amplitude */
- {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
- 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
- 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
- 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, },
- /* single decay */
- {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- /* repetitive decay */
- {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
- /* single triangular */
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- /* repetitive triangular */
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 },
- /* single attack */
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- /* repetitive attack */
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 }
-};
-
-static int amplitude_lookup[16] = {
- 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16,
- 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16,
- 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16,
- 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16
-};
-
-class CMSEmulator {
-public:
- CMSEmulator(uint32 sampleRate) {
- _sampleRate = sampleRate;
- memset(_saa1099, 0, sizeof(SAA1099)*2);
- }
-
- ~CMSEmulator() { }
-
- void portWrite(int port, int val);
- void readBuffer(int16 *buffer, const int numSamples);
-private:
- uint32 _sampleRate;
-
- SAA1099 _saa1099[2];
-
- void envelope(int chip, int ch);
- void update(int chip, int16 *buffer, int length);
- void portWriteIntern(int chip, int offset, int data);
-};
-
-void CMSEmulator::portWrite(int port, int val) {
- switch (port) {
- case 0x220:
- portWriteIntern(0, 1, val);
- break;
-
- case 0x221:
- _saa1099[0].selected_reg = val & 0x1f;
- if (_saa1099[0].selected_reg == 0x18 || _saa1099[0].selected_reg == 0x19) {
- /* clock the envelope channels */
- if (_saa1099[0].env_clock[0]) envelope(0, 0);
- if (_saa1099[0].env_clock[1]) envelope(0, 1);
- }
- break;
-
- case 0x222:
- portWriteIntern(1, 1, val);
- break;
-
- case 0x223:
- _saa1099[1].selected_reg = val & 0x1f;
- if (_saa1099[1].selected_reg == 0x18 || _saa1099[1].selected_reg == 0x19) {
- /* clock the envelope channels */
- if (_saa1099[1].env_clock[0]) envelope(1, 0);
- if (_saa1099[1].env_clock[1]) envelope(1, 1);
- }
- break;
-
- default:
- warning("CMSEmulator got port: 0x%X", port);
- break;
- }
-}
-
-void CMSEmulator::readBuffer(int16 *buffer, const int numSamples) {
- update(0, &buffer[0], numSamples);
- update(1, &buffer[0], numSamples);
-}
-
-void CMSEmulator::envelope(int chip, int ch) {
- SAA1099 *saa = &_saa1099[chip];
- if (saa->env_enable[ch]) {
- int step, mode, mask;
- mode = saa->env_mode[ch];
- /* step from 0..63 and then loop in steps 32..63 */
- step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20);
-
- mask = 15;
- if (saa->env_bits[ch])
- mask &= ~1; /* 3 bit resolution, mask LSB */
-
- saa->channels[ch*3+0].envelope[ LEFT] =
- saa->channels[ch*3+1].envelope[ LEFT] =
- saa->channels[ch*3+2].envelope[ LEFT] = Scumm::envelope[mode][step] & mask;
- if (saa->env_reverse_right[ch] & 0x01) {
- saa->channels[ch*3+0].envelope[RIGHT] =
- saa->channels[ch*3+1].envelope[RIGHT] =
- saa->channels[ch*3+2].envelope[RIGHT] = (15 - Scumm::envelope[mode][step]) & mask;
- } else {
- saa->channels[ch*3+0].envelope[RIGHT] =
- saa->channels[ch*3+1].envelope[RIGHT] =
- saa->channels[ch*3+2].envelope[RIGHT] = Scumm::envelope[mode][step] & mask;
- }
- } else {
- /* envelope mode off, set all envelope factors to 16 */
- saa->channels[ch*3+0].envelope[ LEFT] =
- saa->channels[ch*3+1].envelope[ LEFT] =
- saa->channels[ch*3+2].envelope[ LEFT] =
- saa->channels[ch*3+0].envelope[RIGHT] =
- saa->channels[ch*3+1].envelope[RIGHT] =
- saa->channels[ch*3+2].envelope[RIGHT] = 16;
- }
-}
-
-void CMSEmulator::update(int chip, int16 *buffer, int length) {
- struct SAA1099 *saa = &_saa1099[chip];
- int j, ch;
-
- /* if the channels are disabled we're done */
- if (!saa->all_ch_enable) {
- /* init output data */
- if (chip == 0) {
- memset(buffer, 0, sizeof(int16)*length*2);
- }
- return;
- }
-
- if (chip == 0) {
- memset(buffer, 0, sizeof(int16)*length*2);
- }
-
- for (ch = 0; ch < 2; ch++) {
- switch (saa->noise_params[ch]) {
- case 0: saa->noise[ch].freq = 31250.0 * 2; break;
- case 1: saa->noise[ch].freq = 15625.0 * 2; break;
- case 2: saa->noise[ch].freq = 7812.5 * 2; break;
- case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break;
- }
- }
-
- /* fill all data needed */
- for (j = 0; j < length; ++j) {
- int output_l = 0, output_r = 0;
-
- /* for each channel */
- for (ch = 0; ch < 6; ch++) {
- if (saa->channels[ch].freq == 0.0)
- saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) /
- (511.0 - (double)saa->channels[ch].frequency);
-
- /* check the actual position in the square wave */
- saa->channels[ch].counter -= saa->channels[ch].freq;
- while (saa->channels[ch].counter < 0) {
- /* calculate new frequency now after the half wave is updated */
- saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) /
- (511.0 - (double)saa->channels[ch].frequency);
-
- saa->channels[ch].counter += _sampleRate;
- saa->channels[ch].level ^= 1;
-
- /* eventually clock the envelope counters */
- if (ch == 1 && saa->env_clock[0] == 0)
- envelope(chip, 0);
- if (ch == 4 && saa->env_clock[1] == 0)
- envelope(chip, 1);
- }
-
- /* if the noise is enabled */
- if (saa->channels[ch].noise_enable) {
- /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */
- if (saa->noise[ch/3].level & 1) {
- /* subtract to avoid overflows, also use only half amplitude */
- output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2;
- output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2;
- }
- }
-
- /* if the square wave is enabled */
- if (saa->channels[ch].freq_enable) {
- /* if the channel level is high */
- if (saa->channels[ch].level & 1) {
- output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16;
- output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16;
- }
- }
- }
-
- for (ch = 0; ch < 2; ch++) {
- /* check the actual position in noise generator */
- saa->noise[ch].counter -= saa->noise[ch].freq;
- while (saa->noise[ch].counter < 0) {
- saa->noise[ch].counter += _sampleRate;
- if (((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) )
- saa->noise[ch].level = (saa->noise[ch].level << 1) | 1;
- else
- saa->noise[ch].level <<= 1;
- }
- }
- /* write sound data to the buffer */
- buffer[j*2] += output_l / 6;
- buffer[j*2+1] += output_r / 6;
- }
-}
-
-void CMSEmulator::portWriteIntern(int chip, int offset, int data) {
- SAA1099 *saa = &_saa1099[chip];
- int reg = saa->selected_reg;
- int ch;
-
- switch (reg) {
- /* channel i amplitude */
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- ch = reg & 7;
- saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f];
- saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f];
- break;
-
- /* channel i frequency */
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- ch = reg & 7;
- saa->channels[ch].frequency = data & 0xff;
- break;
-
- /* channel i octave */
- case 0x10:
- case 0x11:
- case 0x12:
- ch = (reg - 0x10) << 1;
- saa->channels[ch + 0].octave = data & 0x07;
- saa->channels[ch + 1].octave = (data >> 4) & 0x07;
- break;
-
- /* channel i frequency enable */
- case 0x14:
- saa->channels[0].freq_enable = data & 0x01;
- saa->channels[1].freq_enable = data & 0x02;
- saa->channels[2].freq_enable = data & 0x04;
- saa->channels[3].freq_enable = data & 0x08;
- saa->channels[4].freq_enable = data & 0x10;
- saa->channels[5].freq_enable = data & 0x20;
- break;
-
- /* channel i noise enable */
- case 0x15:
- saa->channels[0].noise_enable = data & 0x01;
- saa->channels[1].noise_enable = data & 0x02;
- saa->channels[2].noise_enable = data & 0x04;
- saa->channels[3].noise_enable = data & 0x08;
- saa->channels[4].noise_enable = data & 0x10;
- saa->channels[5].noise_enable = data & 0x20;
- break;
-
- /* noise generators parameters */
- case 0x16:
- saa->noise_params[0] = data & 0x03;
- saa->noise_params[1] = (data >> 4) & 0x03;
- break;
-
- /* envelope generators parameters */
- case 0x18:
- case 0x19:
- ch = reg - 0x18;
- saa->env_reverse_right[ch] = data & 0x01;
- saa->env_mode[ch] = (data >> 1) & 0x07;
- saa->env_bits[ch] = data & 0x10;
- saa->env_clock[ch] = data & 0x20;
- saa->env_enable[ch] = data & 0x80;
- /* reset the envelope */
- saa->env_step[ch] = 0;
- break;
-
- /* channels enable & reset generators */
- case 0x1c:
- saa->all_ch_enable = data & 0x01;
- saa->sync_state = data & 0x02;
- if (data & 0x02) {
- int i;
- /* Synch & Reset generators */
- for (i = 0; i < 6; i++) {
- saa->channels[i].level = 0;
- saa->channels[i].counter = 0.0;
- }
- }
- break;
-
- default: /* Error! */
- error("CMS Unkown write to reg %x with %x",reg, data);
- }
-}
-
-#pragma mark -
-#pragma mark - Player_V2CMS
-#pragma mark -
+#define CMS_RATE 22050
const uint8 note_lengths[] = {
0,
diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp
index 014787ec7e..02b2482e40 100644
--- a/engines/scumm/room.cpp
+++ b/engines/scumm/room.cpp
@@ -194,6 +194,11 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) {
showActors();
_egoPositioned = false;
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ towns_resetPalCycleFields();
+#endif
+
runEntryScript();
if (_game.version >= 1 && _game.version <= 2) {
runScript(5, 0, 0, 0);
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 81762d87a8..b100e15604 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -32,6 +32,7 @@
#include "scumm/charset.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/imuse/imuse.h"
+#include "player_towns.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
@@ -446,6 +447,9 @@ bool ScummEngine::loadState(int slot, bool compat) {
// Update volume settings
syncSoundSettings();
+ if (_townsPlayer && (hdr.ver >= VER(81)))
+ _townsPlayer->restoreAfterLoad();
+
// Init NES costume data
if (_game.platform == Common::kPlatformNES) {
if (hdr.ver < VER(47))
@@ -1289,9 +1293,38 @@ void ScummEngine::saveOrLoad(Serializer *s) {
//
// Save/load palette data
//
- if (_16BitPalette) {
+ if (_16BitPalette && !(_game.platform == Common::kPlatformFMTowns && s->isLoading() && s->getVersion() < VER(82))) {
s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16);
}
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ // FM-Towns specific (extra palette data, color cycle data, etc.)
+ if (s->getVersion() >= VER(82)) {
+ const SaveLoadEntry townsFields[] = {
+ MKLINE(Common::Rect, left, sleInt16, VER(82)),
+ MKLINE(Common::Rect, top, sleInt16, VER(82)),
+ MKLINE(Common::Rect, right, sleInt16, VER(82)),
+ MKLINE(Common::Rect, bottom, sleInt16, VER(82)),
+ MKEND()
+ };
+
+ const SaveLoadEntry townsExtraEntries[] = {
+ MKLINE(ScummEngine, _townsOverrideShadowColor, sleUint8, VER(82)),
+ MKLINE(ScummEngine, _numCyclRects, sleUint8, VER(82)),
+ MKLINE(ScummEngine, _townsPaletteFlags, sleUint8, VER(82)),
+ MKLINE(ScummEngine, _townsClearLayerFlag, sleUint8, VER(82)),
+ MKLINE(ScummEngine, _townsActiveLayerFlags, sleUint8, VER(82)),
+ MKEND()
+ };
+
+ s->saveLoadArrayOf(_textPalette, 48, sizeof(_textPalette[0]), sleUint8);
+ s->saveLoadArrayOf(_cyclRects, 10, sizeof(_cyclRects[0]), townsFields);
+ s->saveLoadArrayOf(&_curStringRect, 1, sizeof(_curStringRect), townsFields);
+ s->saveLoadArrayOf(_townsCharsetColorMap, 16, sizeof(_townsCharsetColorMap[0]), sleUint8);
+ s->saveLoadEntries(this, townsExtraEntries);
+ }
+#endif
+
if (_shadowPaletteSize) {
s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte);
// _roomPalette didn't show up until V21 save games
@@ -1393,6 +1426,11 @@ void ScummEngine::saveOrLoad(Serializer *s) {
_imuse->save_or_load(s, this);
}
+
+ // Save/load FM-Towns audio status
+ if (_townsPlayer)
+ _townsPlayer->saveLoadWithSerializer(s);
+
//
// Save/load the charset renderer state
//
@@ -1449,6 +1487,17 @@ void ScummEngine_v5::saveOrLoad(Serializer *s) {
// This is probably only needed for Loom.
s->saveLoadEntries(this, cursorEntries);
+
+ // Reset cursors for old FM-Towns savegames saved with 256 color setting.
+ // Otherwise the cursor will be messed up when displayed in the new hi color setting.
+ if (_game.platform == Common::kPlatformFMTowns && _bytesPerPixelOutput == 2 && s->isLoading() && s->getVersion() < VER(82)) {
+ if (_game.id == GID_LOOM) {
+ redefineBuiltinCursorFromChar(1, 1);
+ redefineBuiltinCursorHotspot(1, 0, 0);
+ } else {
+ resetCursors();
+ }
+ }
}
#ifdef ENABLE_SCUMM_7_8
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
index fafb6b383f..d33ece7f6a 100644
--- a/engines/scumm/saveload.h
+++ b/engines/scumm/saveload.h
@@ -50,7 +50,7 @@ namespace Scumm {
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
-#define CURRENT_VER 80
+#define CURRENT_VER 83
/**
* An auxillary macro, used to specify savegame versions. We use this instead
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 29f0c025d2..223e9822e2 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -708,25 +708,6 @@ void ScummEngine::writeVar(uint var, int value) {
error("Illegal varbits (w)");
}
-void ScummEngine_v5::getResultPos() {
- int a;
-
- _resultVarNumber = fetchScriptWord();
- if (_resultVarNumber & 0x2000) {
- a = fetchScriptWord();
- if (a & 0x2000) {
- _resultVarNumber += readVar(a & ~0x2000);
- } else {
- _resultVarNumber += a & 0xFFF;
- }
- _resultVarNumber &= ~0x2000;
- }
-}
-
-void ScummEngine_v5::setResult(int value) {
- writeVar(_resultVarNumber, value);
-}
-
void ScummEngine::push(int a) {
assert(_scummStackPos >= 0 && _scummStackPos < ARRAYSIZE(_vmStack));
_vmStack[_scummStackPos++] = a;
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index d93e2ea3e4..bc8446d16f 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -640,7 +640,6 @@ void ScummEngine_v2::o2_waitForActor() {
}
void ScummEngine_v2::o2_waitForMessage() {
-
if (VAR(VAR_HAVE_MSG)) {
_scriptPointer--;
o5_breakHere();
diff --git a/engines/scumm/script_v3.cpp b/engines/scumm/script_v3.cpp
index 7d2efdb9a8..176eefdeef 100644
--- a/engines/scumm/script_v3.cpp
+++ b/engines/scumm/script_v3.cpp
@@ -24,6 +24,7 @@
*/
#include "scumm/scumm_v3.h"
+#include "scumm/actor.h"
namespace Scumm {
@@ -36,6 +37,11 @@ void ScummEngine_v3::setupOpcodes() {
OPCODE(0x30, o3_setBoxFlags);
OPCODE(0xb0, o3_setBoxFlags);
}
+
+ OPCODE(0x3b, o3_waitForActor);
+ OPCODE(0xbb, o3_waitForActor);
+
+ OPCODE(0x4c, o3_waitForSentence);
}
void ScummEngine_v3::o3_setBoxFlags() {
@@ -46,4 +52,38 @@ void ScummEngine_v3::o3_setBoxFlags() {
setBoxFlags(a, b);
}
+void ScummEngine_v3::o3_waitForActor() {
+ // This opcode was a NOP in LOOM. Also, we cannot directly use
+ // o2_waitForActor because there the _scriptPointer is different (it
+ // assumes that getVar reads only a single byte, which is correct
+ // for v2 but not for v3). Of course we could copy the code here to
+ // o2_waitForActor and then merge the two, but right now I am
+ // keeping this as it is.
+ if (_game.id == GID_INDY3) {
+ const byte *oldaddr = _scriptPointer - 1;
+ Actor *a = derefActor(getVarOrDirectByte(PARAM_1), "o3_waitForActor");
+ if (a->_moving) {
+ _scriptPointer = oldaddr;
+ o5_breakHere();
+ }
+ }
+}
+
+void ScummEngine_v3::o3_waitForSentence() {
+ // FIXME/TODO: Can we merge this with o2_waitForSentence? I think
+ // the code here is actually incorrect, and the code in
+ // o2_waitForSentence correct, but somebody should check the
+ // disassembly for Indy and Loom.
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ } else if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+
+ _scriptPointer--;
+ o5_breakHere();
+}
+
+
+
} // End of namespace Scumm
diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index 01927b02e7..b6e5834acc 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -60,6 +60,11 @@ void ScummEngine_v4::setupOpcodes() {
OPCODE(0x22, o4_saveLoadGame);
OPCODE(0xa2, o4_saveLoadGame);
+
+ // Disable some opcodes which are unused in v4.
+ _opcodes[0x3b].setProc(0, 0);
+ _opcodes[0x4c].setProc(0, 0);
+ _opcodes[0xbb].setProc(0, 0);
}
void ScummEngine_v4::o4_ifState() {
@@ -106,62 +111,17 @@ void ScummEngine_v4::o4_oldRoomEffect() {
if ((_opcode & 0x1F) == 3) {
a = getVarOrDirectWord(PARAM_1);
-#if 1
if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
- // FIXME / TODO: OK the first thing to note is: at least in Zak256,
- // maybe also in other games, this opcode does a bit more. I added
- // some stubs here, but somebody with a full IDA or more knowledge
- // about this will have to fill in the gaps. At least now we know
- // that something is missing here :-)
-
if (a == 4) {
- //printf("o5_oldRoomEffect ODDBALL: _opcode = 0x%x, a = 0x%x\n", _opcode, a);
- // No idea what byte_2FCCF is, but it's a globale boolean flag.
- // I only add it here as a temporary hack to make the pseudo code compile.
- // Maybe it is just there as a reentry protection guard, given
- // how it is used? It might also correspond to _screenEffectFlag.
- int byte_2FCCF = 0;
-
- // For now, we force a redraw of the screen background. This
- // way the Zak end credits seem to work mostly correct.
- VirtScreen *vs = &_virtscr[kMainVirtScreen];
- restoreBackground(Common::Rect(0, vs->topline, vs->w, vs->topline + vs->h));
- vs->setDirtyRange(0, vs->h);
- updateDirtyScreen(kMainVirtScreen);
-
- if (byte_2FCCF) {
- // Here now "sub_1C44" is called, which sets byte_2FCCF to 0 then
- // calls yet another sub (which also reads byte_2FCCF):
-
- byte_2FCCF = 0;
- //call sub_0BB3
-
-
- // Now sub_085C is called. This is quite simply: it sets
- // 0xF000 bytes. starting at 0x40000 to 0. No idea what that
- // buffer is, maybe a screen buffer, though. Note that
- // 0xF000 = 320*192.
- // Maybe this is also the charset mask being cleaned?
-
- // call sub_085C
-
-
- // And then sub_1C54 is called, which is almost identical to
- // the above sub_1C44, only it sets byte_2FCCF to 1:
-
- byte_2FCCF = 1;
- // call sub_0BB3
-
- } else {
- // Here only sub_085C is called (see comment above)
-
- // call sub_085C
- }
- return;
- }
+ _textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->clearLayer(1);
#endif
-
+ return;
+ }
}
+
if (a) {
_switchRoomEffect = (byte)(a & 0xFF);
_switchRoomEffect2 = (byte)(a >> 8);
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index 5c20e0dfd3..ea903fc108 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -29,6 +29,7 @@
#include "scumm/scumm_v3.h"
#include "scumm/scumm_v5.h"
#include "scumm/sound.h"
+#include "scumm/player_towns.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
@@ -377,6 +378,25 @@ int ScummEngine_v5::getVarOrDirectWord(byte mask) {
return fetchScriptWordSigned();
}
+void ScummEngine_v5::getResultPos() {
+ int a;
+
+ _resultVarNumber = fetchScriptWord();
+ if (_resultVarNumber & 0x2000) {
+ a = fetchScriptWord();
+ if (a & 0x2000) {
+ _resultVarNumber += readVar(a & ~0x2000);
+ } else {
+ _resultVarNumber += a & 0xFFF;
+ }
+ _resultVarNumber &= ~0x2000;
+ }
+}
+
+void ScummEngine_v5::setResult(int value) {
+ writeVar(_resultVarNumber, value);
+}
+
void ScummEngine_v5::jumpRelative(bool cond) {
// We explicitly call ScummEngine::fetchScriptWord()
// to make this method work also in v0, which overloads
@@ -980,17 +1000,6 @@ void ScummEngine_v5::o5_getActorRoom() {
void ScummEngine_v5::o5_getActorScale() {
Actor *a;
- // INDY3 uses this opcode for waitForActor
- if (_game.id == GID_INDY3) {
- const byte *oldaddr = _scriptPointer - 1;
- a = derefActor(getVarOrDirectByte(PARAM_1), "o5_getActorScale (wait)");
- if (a->_moving) {
- _scriptPointer = oldaddr;
- o5_breakHere();
- }
- return;
- }
-
getResultPos();
int act = getVarOrDirectByte(PARAM_1);
a = derefActor(act, "o5_getActorScale");
@@ -1587,21 +1596,18 @@ void ScummEngine_v5::o5_resourceRoutines() {
debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
break;
case 35:
- // TODO: Might be used to set CD volume in FM-TOWNS Loom
- foo = getVarOrDirectByte(PARAM_2);
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setVolumeCD(getVarOrDirectByte(PARAM_2), resid);
break;
case 36:
- // TODO: Sets the loudness of a sound resource. Used in Indy3 and Zak.
foo = getVarOrDirectByte(PARAM_2);
bar = fetchScriptByte();
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setSoundVolume(resid, foo, bar);
break;
case 37:
- // TODO: Sets the pitch of a sound resource (pitch = foo - center semitones.
- // "center" is at 0x32 in the sfx resource (always 0x3C in zak256, but sometimes different in Indy3).
- foo = getVarOrDirectByte(PARAM_2);
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setSoundNote(resid, getVarOrDirectByte(PARAM_2));
break;
default:
@@ -1611,7 +1617,7 @@ void ScummEngine_v5::o5_resourceRoutines() {
void ScummEngine_v5::o5_roomOps() {
int a = 0, b = 0, c, d, e;
- const bool paramsBeforeOpcode = (_game.version == 3 && _game.platform != Common::kPlatformPCEngine);
+ const bool paramsBeforeOpcode = ((_game.version == 3) && (_game.platform != Common::kPlatformPCEngine));
if (paramsBeforeOpcode) {
a = getVarOrDirectWord(PARAM_1);
@@ -1706,26 +1712,58 @@ void ScummEngine_v5::o5_roomOps() {
case 10: // SO_ROOM_FADE
a = getVarOrDirectWord(PARAM_1);
if (a) {
+ #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
switch (a) {
- case 8: // compose kMainVirtScreen over a screen buffer
- case 9: // call 0x110:0x20 _ax=0x601 _edx=2
- case 10: // call 0x110:0x20 _ax=0x601 _edx=3
- case 11: // clear screen 0x1C:0x45000 sizeof(640 * 320)
- case 12: // call 0x110:0x20 _ax=0x601 _edx=0
- case 13: // call 0x110:0x20 _ax=0x601 _edx=1
- case 16: // enable clearing of a screen buffer in drawBitmap()
- case 17: // disable clearing of a screen buffer in drawBitmap()
- case 18: // clear a screen buffer
+ case 8:
+ towns_drawStripToScreen(&_virtscr[kMainVirtScreen], 0, _virtscr[kMainVirtScreen].topline, 0, 0, _virtscr[kMainVirtScreen].w, _virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h);
+ _townsScreen->update();
+ return;
+ case 9:
+ _townsActiveLayerFlags = 2;
+ _townsScreen->toggleLayers(_townsActiveLayerFlags);
+ return;
+ case 10:
+ _townsActiveLayerFlags = 3;
+ _townsScreen->toggleLayers(_townsActiveLayerFlags);
+ return;
+ case 11:
+ _townsScreen->clearLayer(1);
+ return;
+ case 12:
+ _townsActiveLayerFlags = 0;
+ _townsScreen->toggleLayers(_townsActiveLayerFlags);
+ return;
+ case 13:
+ _townsActiveLayerFlags = 1;
+ _townsScreen->toggleLayers(_townsActiveLayerFlags);
+ return;
+ case 16: // enable clearing of layer 2 buffer in drawBitmap()
+ _townsPaletteFlags |= 2;
+ return;
+ case 17: // disable clearing of layer 2 buffer in drawBitmap()
+ _townsPaletteFlags &= ~2;
+ return;
+ case 18: // clear kMainVirtScreen layer 2 buffer
+ _textSurface.fillRect(Common::Rect(0, _virtscr[kMainVirtScreen].topline * _textSurfaceMultiplier, _textSurface.pitch, (_virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h) * _textSurfaceMultiplier), 0);
case 19: // enable palette operations (palManipulate(), cyclePalette() etc.)
+ _townsPaletteFlags |= 1;
+ return;
case 20: // disable palette operations
- case 21: // disable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
- case 22: // enable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
+ _townsPaletteFlags &= ~1;
+ return;
+ case 21: // disable clearing of layer 0 in initScreens()
+ _townsClearLayerFlag = 1;
+ return;
+ case 22: // enable clearing of layer 0 in initScreens()
+ _townsClearLayerFlag = 0;
+ return;
case 30:
- debug(0, "o5_roomOps: unhandled FM-TOWNS fadeEffect %d", a);
+ _townsOverrideShadowColor = 3;
return;
}
}
+#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
_switchRoomEffect = (byte)(a & 0xFF);
_switchRoomEffect2 = (byte)(a >> 8);
} else {
@@ -1972,7 +2010,7 @@ void ScummEngine_v5::o5_startMusic() {
result = _sound->getCurrentCDSound();
break;
case 0xFF:
- // TODO: Might return current CD volume in FM-TOWNS Loom. See also bug #805691.
+ result = _townsPlayer->getCurrentCdaVolume();
break;
default:
// TODO: return track length in seconds. We'll have to extend Sound and OSystem for this.
@@ -2025,19 +2063,6 @@ void ScummEngine_v5::o5_isSoundRunning() {
void ScummEngine_v5::o5_soundKludge() {
int items[16];
-
- if (_game.features & GF_SMALL_HEADER) { // Is WaitForSentence in SCUMM V3
- if (_sentenceNum) {
- if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
- return;
- } else if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
- return;
-
- _scriptPointer--;
- o5_breakHere();
- return;
- }
-
int num = getWordVararg(items);
_sound->soundKludge(items, num);
}
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index a25fac1a88..b141b2d758 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 Jun 27 05:23:26 2010
+ This file was generated by the md5table tool on Sat Oct 02 14:51:12 2010
DO NOT EDIT MANUALLY!
*/
@@ -117,7 +117,7 @@ static const MD5Table md5table[] = {
{ "2723fea3dae0cb47768c424b145ae0e7", "tentacle", "Floppy", "Floppy", 7932, Common::EN_ANY, Common::kPlatformPC },
{ "27b2ef1653089fe5b897d9cc89ce784f", "balloon", "HE 80", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "27b3a4224ad63d5b04627595c1c1a025", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformAmiga },
- { "28d24a33448fab6795850bc9f159a4a2", "atlantis", "", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },
+ { "28d24a33448fab6795850bc9f159a4a2", "atlantis", "FM-TOWNS", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },
{ "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformPC },
{ "28f07458f1b6c24e118a1ea056827701", "lost", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "2a208ffbcd0e83e86f4356e6f64aa6e1", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC },
@@ -183,7 +183,7 @@ static const MD5Table md5table[] = {
{ "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 },
+ { "430bc518017b6fac046f58bab6baad5d", "monkey2", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 },
{ "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown },
{ "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC },
@@ -206,7 +206,7 @@ static const MD5Table md5table[] = {
{ "4c4820518e16e1a0e3616a3b021a04f3", "catalog", "HE CUP", "Preview", 10927456, Common::DE_DEU, Common::kPlatformUnknown },
{ "4cb9c3618f71668f8e4346c8f323fa82", "monkey2", "", "", 10700, Common::EN_ANY, Common::kPlatformMacintosh },
{ "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows },
- { "4d34042713958b971cb139fba4658586", "atlantis", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
+ { "4d34042713958b971cb139fba4658586", "atlantis", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "4dc780f1bc587a193ce8a97652791438", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformAmiga },
{ "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", "HE 90", "Demo", 18394, Common::EN_USA, Common::kPlatformUnknown },
@@ -501,7 +501,7 @@ static const MD5Table md5table[] = {
{ "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", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "FM-TOWNS", "", 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 },
@@ -552,7 +552,7 @@ static const MD5Table md5table[] = {
{ "d8323015ecb8b10bf53474f6e6b0ae33", "dig", "", "", 16304, Common::UNK_LANG, Common::kPlatformUnknown },
{ "d917f311a448e3cc7239c31bddb00dd2", "samnmax", "", "CD", 9080, Common::EN_ANY, Common::kPlatformUnknown },
{ "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", "", "Demo", 6478, Common::EN_ANY, Common::kPlatformPC },
- { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "FM-TOWNS", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns },
{ "da6269b18fcb08189c0aa9c95533cce2", "monkey", "CD", "CD", 8955, Common::IT_ITA, Common::kPlatformPC },
{ "da669b20271b85182e9c17a2a37ea02e", "monkey2", "", "", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "db21a6e338fe3b70c2723b6530865bf2", "PuttTime", "HE 85", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 50901b8f9e..2f25aeefba 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -29,6 +29,7 @@
#include "common/events.h"
#include "common/EventRecorder.h"
#include "common/system.h"
+#include "common/translation.h"
#include "engines/util.h"
@@ -48,6 +49,7 @@
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/smush/smush_mixer.h"
#include "scumm/smush/smush_player.h"
+#include "scumm/player_towns.h"
#include "scumm/insane/insane.h"
#include "scumm/he/animation_he.h"
#include "scumm/he/intern_he.h"
@@ -146,6 +148,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
+ _townsPlayer = NULL;
_verbs = NULL;
_objs = NULL;
_sound = NULL;
@@ -255,7 +258,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_switchRoomEffect2 = 0;
_switchRoomEffect = 0;
- _bytesPerPixel = 1;
+ _bytesPerPixelOutput = _bytesPerPixel = 1;
_doEffect = false;
_snapScroll = false;
_currentLights = 0;
@@ -275,6 +278,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_hePalettes = NULL;
_hePaletteSlot = 0;
_16BitPalette = NULL;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _townsScreen = 0;
+#endif
_shadowPalette = NULL;
_shadowPaletteSize = 0;
memset(_currentPalette, 0, sizeof(_currentPalette));
@@ -315,6 +321,15 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_skipDrawObject = 0;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _townsPaletteFlags = 0;
+ _townsClearLayerFlag = 1;
+ _townsActiveLayerFlags = 3;
+ memset(&_curStringRect, -1, sizeof(Common::Rect));
+ memset(&_cyclRects, 0, 16 * sizeof(Common::Rect));
+ _numCyclRects = 0;
+#endif
+
//
// Init all VARS to 0xFF
//
@@ -530,16 +545,21 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_screenHeight = 200;
}
- _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1;
+ _bytesPerPixelOutput = _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1;
+
+#ifdef USE_RGB_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns)
+ _bytesPerPixelOutput = 2;
+#endif
+#endif
// Allocate gfx compositing buffer (not needed for V7/V8 games).
if (_game.version < 7)
- _compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixel);
+ _compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixelOutput);
else
_compositeBuf = 0;
- _fmtownsBuf = 0;
-
_herculesBuf = 0;
if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
_herculesBuf = (byte *)malloc(Common::kHercW * Common::kHercH);
@@ -605,7 +625,12 @@ ScummEngine::~ScummEngine() {
free(_compositeBuf);
free(_herculesBuf);
- free(_fmtownsBuf);
+
+ free(_16BitPalette);
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ delete _townsScreen;
+#endif
delete _debugger;
@@ -695,6 +720,10 @@ ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr)
_activeObject2Inv = false;
_activeObjectObtained = false;
_activeObject2Obtained = false;
+
+ VAR_ACTIVE_ACTOR = 0xFF;
+ VAR_IS_SOUND_RUNNING = 0xFF;
+ VAR_ACTIVE_VERB = 0xFF;
}
ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr)
@@ -1109,16 +1138,29 @@ Common::Error ScummEngine::init() {
screenWidth *= _textSurfaceMultiplier;
screenHeight *= _textSurfaceMultiplier;
}
- if (_game.features & GF_16BIT_COLOR) {
+ if (_game.features & GF_16BIT_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ || _game.platform == Common::kPlatformFMTowns
+#endif
+ ) {
#ifdef USE_RGB_COLOR
Graphics::PixelFormat format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
initGraphics(screenWidth, screenHeight, screenWidth > 320, &format);
if (format != _system->getScreenFormat())
return Common::kUnsupportedColorMode;
#else
- error("16bit color support is required for this game");
+ if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) {
+ warning("Starting game without the required 16bit color support.\nYou may experience color glitches");
+ initGraphics(screenWidth, screenHeight, (screenWidth > 320));
+ } else {
+ error("16bit color support is required for this game");
+ }
#endif
} else {
+#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && _game.version == 5)
+ error("This game requires dual graphics layer support which is disabled in this build");
+#endif
initGraphics(screenWidth, screenHeight, (screenWidth > 320));
}
}
@@ -1234,13 +1276,8 @@ void ScummEngine::setupScumm() {
_res->setHeapThreshold(400000, maxHeapThreshold);
- if (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) {
- free(_fmtownsBuf);
- _fmtownsBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier);
- }
-
free(_compositeBuf);
- _compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixel);
+ _compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixelOutput);
}
#ifdef ENABLE_SCUMM_7_8
@@ -1318,6 +1355,24 @@ void ScummEngine::resetScumm() {
debug(9, "resetScumm");
+#ifdef USE_RGB_COLOR
+ if (_game.features & GF_16BIT_COLOR
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ || _game.platform == Common::kPlatformFMTowns
+#endif
+ )
+ _16BitPalette = (uint16 *)calloc(512, sizeof(uint16));
+#endif
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns) {
+ delete _townsScreen;
+ _townsScreen = new TownsScreen(_system, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, _bytesPerPixelOutput);
+ _townsScreen->setupLayer(0, _screenWidth, _screenHeight, (_bytesPerPixelOutput == 2) ? 32767 : 256);
+ _townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 16, _textPalette);
+ }
+#endif
+
if (_game.version == 0) {
initScreens(8, 144);
} else if ((_game.id == GID_MANIAC) && (_game.version <= 1) && !(_game.platform == Common::kPlatformNES)) {
@@ -1510,8 +1565,6 @@ void ScummEngine_v3::resetScumm() {
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
- _16BitPalette = (uint16 *)calloc(512, sizeof(uint16));
-
// Load tile set and palette for the distaff
byte *roomptr = getResourceAddress(rtRoom, 90);
assert(roomptr);
@@ -1757,6 +1810,10 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = new Player_V2CMS(this, _mixer);
} else if (_game.platform == Common::kPlatform3DO && _game.heversion <= 62) {
// 3DO versions use digital music and sound samples.
+ } else if (_game.platform == Common::kPlatformFMTowns && (_game.version == 3 || _game.id == GID_MONKEY)) {
+ _musicEngine = _townsPlayer = new Player_Towns_v1(this, _mixer);
+ if (!_townsPlayer->init())
+ error("Failed to initialize FM-Towns audio driver");
} else if (_game.version >= 3 && _game.heversion <= 62) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
@@ -1771,7 +1828,16 @@ void ScummEngine::setupMusic(int midi) {
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
}
- _musicEngine = _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
+ _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
+
+ if (_game.platform == Common::kPlatformFMTowns) {
+ _musicEngine = _townsPlayer = new Player_Towns_v2(this, _imuse, _mixer, true);
+ if (!_townsPlayer->init())
+ error("Failed to initialize FM-Towns audio driver");
+ } else {
+ _musicEngine = _imuse;
+ }
+
if (_imuse) {
_imuse->addSysexHandler
(/*IMUSE_SYSEX_ID*/ 0x7D,
@@ -1780,17 +1846,17 @@ void ScummEngine::setupMusic(int midi) {
if (ConfMan.hasKey("tempo"))
_imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo"));
// YM2162 driver can't handle midi->getPercussionChannel(), NULL shouldn't init MT-32/GM/GS
- if ((midi != MDT_TOWNS) && (midi != MDT_NONE)) {
+ if (/*(midi != MDT_TOWNS) && (*/midi != MDT_NONE/*)*/) {
_imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32);
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) {
+ if (_game.heversion >= 60 /*|| midi == MDT_TOWNS*/) {
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
- if (midi == MDT_TOWNS)
- _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
+ /*if (midi == MDT_TOWNS)
+ _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);*/
}
}
}
@@ -1802,10 +1868,23 @@ void ScummEngine::syncSoundSettings() {
int soundVolumeSfx = ConfMan.getInt("sfx_volume");
int soundVolumeSpeech = ConfMan.getInt("speech_volume");
+ bool mute = false;
+
+ if (ConfMan.hasKey("mute")) {
+ mute = ConfMan.getBool("mute");
+
+ if (mute)
+ soundVolumeMusic = soundVolumeSfx = soundVolumeSpeech = 0;
+ }
+
if (_musicEngine) {
_musicEngine->setMusicVolume(soundVolumeMusic);
}
+ if (_townsPlayer) {
+ _townsPlayer->setSfxVolume(soundVolumeSfx);
+ }
+
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSfx);
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
@@ -1904,6 +1983,12 @@ void ScummEngine::waitForTimer(int msec_delay) {
while (!shouldQuit()) {
_sound->updateCD(); // Loop CD Audio if needed
parseEvents();
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->update();
+#endif
+
_system->updateScreen();
if (_system->getMillis() >= start_time + msec_delay)
break;
@@ -1911,6 +1996,12 @@ void ScummEngine::waitForTimer(int msec_delay) {
}
}
+void ScummEngine_v0::scummLoop(int delta) {
+ VAR(VAR_IS_SOUND_RUNNING) = (_sound->_lastSound && _sound->isSoundRunning(_sound->_lastSound) != 0);
+
+ ScummEngine::scummLoop(delta);
+}
+
void ScummEngine::scummLoop(int delta) {
if (_game.version >= 3) {
VAR(VAR_TMR_1) += delta;
@@ -2025,6 +2116,10 @@ load_game:
goto load_game;
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ towns_processPalCycleField();
+#endif
+
if (_currentRoom == 0) {
if (_game.version > 3)
CHARSET_1();
@@ -2094,11 +2189,18 @@ void ScummEngine::scummLoop_updateScummVars() {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
} else if (_game.platform == Common::kPlatformNES) {
+#if 0
// 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;
+ // But do not do it when only scrolling right to left, since otherwise Ed will not show
+ // up on the doorbell (Bug #3039004)
+ if (VAR(VAR_CAMERA_POS_X) < (camera._cur.x >> V12_X_SHIFT) + 2)
+ VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT) + 2;
+ else
+#endif
+ VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT);
} else if (_game.version <= 2) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x >> V12_X_SHIFT;
} else {
@@ -2143,14 +2245,14 @@ void ScummEngine::scummLoop_handleSaveLoad() {
if (_saveLoadFlag == 1) {
success = saveState(_saveLoadSlot, _saveTemporaryState);
if (!success)
- errMsg = "Failed to save game state to file:\n\n%s";
+ errMsg = _("Failed to save game state to file:\n\n%s");
if (success && _saveTemporaryState && VAR_GAME_LOADED != 0xFF && _game.version <= 7)
VAR(VAR_GAME_LOADED) = 201;
} else {
success = loadState(_saveLoadSlot, _saveTemporaryState);
if (!success)
- errMsg = "Failed to load game state from file:\n\n%s";
+ errMsg = _("Failed to load game state from file:\n\n%s");
if (success && _saveTemporaryState && VAR_GAME_LOADED != 0xFF)
VAR(VAR_GAME_LOADED) = (_game.version == 8) ? 1 : 203;
@@ -2162,7 +2264,7 @@ void ScummEngine::scummLoop_handleSaveLoad() {
} else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveTemporaryState) {
// Display "Save successful" message, except for auto saves
char buf[256];
- snprintf(buf, sizeof(buf), "Successfully saved game state in file:\n\n%s", filename.c_str());
+ snprintf(buf, sizeof(buf), _("Successfully saved game state in file:\n\n%s"), filename.c_str());
GUI::TimedMessageDialog dialog(buf, 1500);
runDialog(dialog);
@@ -2405,6 +2507,12 @@ void ScummEngine::pauseEngineIntern(bool pause) {
} else {
// Update the screen to make it less likely that the player will see a
// brief cursor palette glitch when the GUI is disabled.
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_townsScreen)
+ _townsScreen->update();
+#endif
+
_system->updateScreen();
// Resume sound & video
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 8c3df21238..aef5cfbec7 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -43,6 +43,17 @@
#include "sound/mididrv.h"
+#ifdef __DS__
+/* This disables the dual layer mode which is used in FM-Towns versions
+ * of SCUMM games and which emulates the behaviour of the original code.
+ * The only purpose is code size reduction for certain backends.
+ * SCUMM 3 (FM-Towns) games will run in normal (DOS VGA) mode, which should
+ * work just fine in most situations. Some glitches might occur. SCUMM 5 games
+ * will not work without dual layer (and 16 bit color) support.
+ */
+#define DISABLE_TOWNS_DUAL_LAYER_MODE
+#endif
+
namespace GUI {
class Dialog;
}
@@ -70,6 +81,7 @@ class CharsetRenderer;
class IMuse;
class IMuseDigital;
class MusicEngine;
+class Player_Towns;
class ScummEngine;
class ScummDebugger;
class Serializer;
@@ -426,6 +438,7 @@ public:
IMuse *_imuse;
IMuseDigital *_imuseDigital;
MusicEngine *_musicEngine;
+ Player_Towns *_townsPlayer;
Sound *_sound;
VerbSlot *_verbs;
@@ -970,6 +983,7 @@ public:
Common::RenderMode _renderMode;
uint8 _bytesPerPixel;
+ uint8 _bytesPerPixelOutput;
protected:
ColorCycle _colorCycle[16]; // Palette cycles
@@ -1042,6 +1056,7 @@ protected:
void setRoomPalette(int pal, int room);
void setPCEPaletteFromPtr(const byte *ptr);
virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+
virtual void setPalColor(int index, int r, int g, int b);
void setDirtyColors(int min, int max);
const byte *findPalInPals(const byte *pal, int index);
@@ -1075,7 +1090,7 @@ protected:
// Screen rendering
byte *_compositeBuf;
byte *_herculesBuf;
- byte *_fmtownsBuf;
+
virtual void drawDirtyScreenParts();
void updateDirtyScreen(VirtScreenNumber slot);
void drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b);
@@ -1219,7 +1234,7 @@ protected:
void restoreCharsetBg();
void clearCharsetMask();
void clearTextSurface();
-
+
virtual void initCharset(int charset);
virtual void printString(int m, const byte *msg);
@@ -1395,6 +1410,38 @@ public:
// Exists both in V7 and in V72HE:
byte VAR_NUM_GLOBAL_OBJS;
+
+ // FM-Towns specific
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+public:
+ bool towns_isRectInStringBox(int x1, int y1, int x2, int y2);
+ byte _townsPaletteFlags;
+ byte _townsCharsetColorMap[16];
+
+protected:
+ void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h);
+#ifdef USE_RGB_COLOR
+ void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+ void towns_setTextPaletteFromPtr(const byte *ptr);
+#endif
+ void towns_setupPalCycleField(int x1, int y1, int x2, int y2);
+ void towns_processPalCycleField();
+ void towns_resetPalCycleFields();
+ void towns_restoreCharsetBg();
+
+ Common::Rect _cyclRects[16];
+ int _numCyclRects;
+
+ Common::Rect _curStringRect;
+
+ byte _townsOverrideShadowColor;
+ byte _textPalette[48];
+ byte _townsClearLayerFlag;
+ byte _townsActiveLayerFlags;
+ static const uint8 _townsLayer2Mask[];
+
+ TownsScreen *_townsScreen;
+#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
};
} // End of namespace Scumm
diff --git a/engines/scumm/scumm_v0.h b/engines/scumm/scumm_v0.h
index 5ef416f650..7b913f7750 100644
--- a/engines/scumm/scumm_v0.h
+++ b/engines/scumm/scumm_v0.h
@@ -62,6 +62,7 @@ protected:
virtual void setupScummVars();
virtual void resetScummVars();
+ virtual void scummLoop(int delta);
virtual void decodeParseString();
virtual void processInput();
@@ -136,6 +137,10 @@ protected:
void o_endCutscene();
void o_beginOverride();
void o_setOwnerOf();
+
+ byte VAR_ACTIVE_ACTOR;
+ byte VAR_IS_SOUND_RUNNING;
+ byte VAR_ACTIVE_VERB;
};
diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h
index be623924f1..0c54308181 100644
--- a/engines/scumm/scumm_v2.h
+++ b/engines/scumm/scumm_v2.h
@@ -103,8 +103,6 @@ protected:
virtual void setBuiltinCursor(int index);
- virtual void runObject(int obj, int entry);
-
/* Version 2 script opcodes */
void o2_actorFromPos();
void o2_actorOps();
diff --git a/engines/scumm/scumm_v3.h b/engines/scumm/scumm_v3.h
index 767069d1bc..abe75cd64d 100644
--- a/engines/scumm/scumm_v3.h
+++ b/engines/scumm/scumm_v3.h
@@ -50,6 +50,8 @@ protected:
/* Version 3 script opcodes */
void o3_setBoxFlags();
+ void o3_waitForActor();
+ void o3_waitForSentence();
};
/**
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 99ab1b23b7..af7bc5f295 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -31,6 +31,7 @@
#include "scumm/file.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/player_towns.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/util.h"
@@ -76,6 +77,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
_curSoundPos(0),
_currentCDSound(0),
_currentMusic(0),
+ _lastSound(0),
_soundsPaused(false),
_sfxMode(0) {
@@ -93,6 +95,7 @@ Sound::~Sound() {
void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) {
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
+ _lastSound = sound;
// HE music resources are in separate file
if (sound <= _vm->_numSounds)
@@ -149,9 +152,10 @@ void Sound::processSoundQueues() {
data[0] >> 8, data[0] & 0xFF,
data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
- if (_vm->_imuse) {
+ if (_vm->_townsPlayer)
+ _vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_townsPlayer->doCommand(num, data);
+ else if (_vm->_imuse)
_vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_imuse->doCommand(num, data);
- }
}
}
_soundQuePos = 0;
@@ -240,7 +244,7 @@ void Sound::playSound(int soundID) {
_mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);
}
// Support for sampled sound effects in Monkey Island 1 and 2
- else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) {
+ else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SBL ')) {
debugC(DEBUG_SOUND, "Using SBL sound effect");
// SBL resources essentially contain VOC sound data.
@@ -309,91 +313,30 @@ void Sound::playSound(int soundID) {
sound = (byte *)malloc(size);
memcpy(sound, ptr + 6, size);
stream = Audio::makeRawStream(sound, size, rate, Audio::FLAG_UNSIGNED);
- _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);
}
- else if ((_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 3) || READ_BE_UINT32(ptr) == MKID_BE('SOUN') || READ_BE_UINT32(ptr) == MKID_BE('TOWS')) {
-
- bool tows = READ_BE_UINT32(ptr) == MKID_BE('TOWS');
- if (_vm->_game.version == 3) {
- size = READ_LE_UINT32(ptr);
- } else {
- size = READ_BE_UINT32(ptr + 4) - 2;
- if (tows)
- size += 8;
+ else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SOUN')) {
+ if (_vm->_game.version != 3)
ptr += 2;
- }
- rate = 11025;
int type = *(ptr + 0x0D);
- int numInstruments;
-
- if (tows)
- type = 0;
- switch (type) {
- case 0: // Sound effect
- numInstruments = *(ptr + 0x14);
- if (tows)
- numInstruments = 1;
- ptr += 0x16;
- size -= 0x16;
-
- while (numInstruments--) {
- int waveSize = READ_LE_UINT32(ptr + 0x0C);
- int loopStart = READ_LE_UINT32(ptr + 0x10) * 2;
- int loopEnd = READ_LE_UINT32(ptr + 0x14) - 1;
- rate = READ_LE_UINT32(ptr + 0x18) * 1000 / 0x62;
- ptr += 0x20;
- size -= 0x20;
- if (size < waveSize) {
- warning("Wrong wave size in sound #%i: %i", soundID, waveSize);
- waveSize = size;
- }
- sound = (byte *)malloc(waveSize);
- for (int x = 0; x < waveSize; x++) {
- byte b = *ptr++;
- if (b < 0x80)
- sound[x] = 0x7F - b;
- else
- sound[x] = b;
- }
- size -= waveSize;
-
- if (loopEnd > 0) {
- Audio::SeekableAudioStream *s = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED);
- stream = new Audio::SubLoopingAudioStream(s, 0, Audio::Timestamp(0, loopStart, rate), Audio::Timestamp(0, loopEnd, rate));
- } else {
- stream = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED);
- }
- _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID, 255, 0);
- }
- break;
- case 1:
- // Music (Euphony format)
- if (_vm->_musicEngine)
- _vm->_musicEngine->startSound(soundID);
- break;
- case 2: // CD track resource
+ if (type == 2) {
+ // CD track resource
ptr += 0x16;
-
- if (soundID == _currentCDSound && pollCD() == 1) {
+ if (soundID == _currentCDSound && pollCD() == 1)
return;
- }
-
- {
- int track = ptr[0];
- int loops = ptr[1];
- int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
- int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
- playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
- }
+ int track = ptr[0];
+ int loops = ptr[1];
+ int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
+ playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
_currentCDSound = soundID;
- break;
- default:
+ } else {
// All other sound types are ignored
- break;
+ warning("Scumm::Sound::playSound: encountered audio resoure with chunk type 'SOUN' and sound type %d", type);
}
}
else if ((_vm->_game.id == GID_LOOM) && (_vm->_game.platform == Common::kPlatformMacintosh)) {
@@ -479,6 +422,9 @@ void Sound::playSound(int soundID) {
if (_vm->_musicEngine) {
_vm->_musicEngine->startSound(soundID);
}
+
+ if (_vm->_townsPlayer)
+ _currentCDSound = _vm->_townsPlayer->getCurrentCdaSound();
}
}
@@ -844,6 +790,7 @@ void Sound::stopAllSounds() {
}
// Clear the (secondary) sound queue
+ _lastSound = 0;
_soundQue2Pos = 0;
memset(_soundQue2, 0, sizeof(_soundQue2));
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 401b1638cc..4fe46f32f0 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -91,6 +91,7 @@ public:
bool _soundsPaused;
byte _sfxMode;
+ uint _lastSound;
public:
Sound(ScummEngine *parent, Audio::Mixer *mixer);
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 30281cb565..5eb2b9d159 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -508,6 +508,11 @@ void ScummEngine::CHARSET_1() {
if (_game.version >= 5)
memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_keepText && _game.platform == Common::kPlatformFMTowns)
+ memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect));
+#endif
+
if (_talkDelay)
return;
@@ -539,7 +544,12 @@ void ScummEngine::CHARSET_1() {
_nextTop = _string[0].ypos + _screenTop;
#endif
} else {
- restoreCharsetBg();
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns)
+ towns_restoreCharsetBg();
+ else
+#endif
+ restoreCharsetBg();
}
}
@@ -660,6 +670,11 @@ void ScummEngine::CHARSET_1() {
}
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3))
+ memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect));
+#endif
+
#ifdef ENABLE_SCUMM_7_8
if (_game.version >= 7 && subtitleLine != subtitleBuffer) {
((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index d1d3ed63a4..5e6e96e413 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -116,10 +116,10 @@ void ScummEngine_v0::setupScummVars() {
VAR_CAMERA_POS_X = 2;
VAR_HAVE_MSG = 3;
VAR_ROOM = 4;
- //VAR_ACTIVE_ACTOR = 5;
+ VAR_ACTIVE_ACTOR = 5;
VAR_OVERRIDE = 6;
- //VAR_IS_SOUND_RUNNING = 8;
- //VAR_ACTIVE_VERB = 9;
+ VAR_IS_SOUND_RUNNING = 8;
+ VAR_ACTIVE_VERB = 9;
VAR_CHARCOUNT = 10;
}
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
index 95fba8ee7e..77181c0b55 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -651,6 +651,50 @@ void ScummEngine_v2::checkExecVerbs() {
}
}
+ // Simulate inventory picking and scrolling keys
+ int object = -1;
+
+ switch (_mouseAndKeyboardStat) {
+ case 'u': // arrow up
+ if (_inventoryOffset >= 2) {
+ _inventoryOffset -= 2;
+ redrawV2Inventory();
+ }
+ return;
+ case 'j': // arrow down
+ if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
+ _inventoryOffset += 2;
+ redrawV2Inventory();
+ }
+ return;
+ case 'i': // object
+ object = 0;
+ break;
+ case 'o':
+ object = 1;
+ break;
+ case 'k':
+ object = 2;
+ break;
+ case 'l':
+ object = 3;
+ break;
+ }
+
+ if (object != -1) {
+ object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
+
+ if (object > 0) {
+ if (_game.version == 0) {
+ _activeInventory = object;
+
+ } else {
+ runInputScript(kInventoryClickArea, object, 0);
+ }
+ }
+ return;
+ }
+
// Generic keyboard input
runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
@@ -701,30 +745,17 @@ void ScummEngine_v0::runObject(int obj, int entry) {
runObjectScript(obj, entry, false, false, NULL);
} else if (entry != 13 && entry != 15) {
if (_activeVerb != 3) {
- VAR(9) = entry;
+ VAR(VAR_ACTIVE_VERB) = entry;
runScript(3, 0, 0, 0);
// For some reasons, certain objects don't have a "give" script
- } else if (VAR(5) > 0 && VAR(5) < 8) {
+ } else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) {
if (_activeInventory)
- setOwnerOf(_activeInventory, VAR(5));
+ setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR));
}
}
}
-void ScummEngine_v2::runObject(int obj, int entry) {
- if (getVerbEntrypoint(obj, entry) != 0) {
- runObjectScript(obj, entry, false, false, NULL);
- } else if (entry != 13 && entry != 15) {
- VAR(9) = entry;
- runScript(3, 0, 0, 0);
- }
-
- _activeInventory = 0;
- _activeObject = 0;
- _activeVerb = 13;
-}
-
bool ScummEngine_v0::verbMoveToActor(int actor) {
Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor");
Actor *a2 = derefActor(actor, "verbMoveToActor");
@@ -911,7 +942,7 @@ bool ScummEngine_v0::verbExec() {
return true;
}
_v0ObjectInInventory = true;
- VAR(5) = _activeActor;
+ VAR(VAR_ACTIVE_ACTOR) = _activeActor;
runObject(_activeInventory , 3);
_v0ObjectInInventory = false;
@@ -1420,9 +1451,14 @@ void ScummEngine::restoreVerbBG(int verb) {
VerbSlot *vs;
vs = &_verbs[verb];
+ uint8 col =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ ((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 :
+#endif
+ vs->bkcolor;
if (vs->oldRect.left != -1) {
- restoreBackground(vs->oldRect, vs->bkcolor);
+ restoreBackground(vs->oldRect, col);
vs->oldRect.left = -1;
}
}
diff --git a/engines/sky/music/gmmusic.cpp b/engines/sky/music/gmmusic.cpp
index f2abb3f277..e7b3a24170 100644
--- a/engines/sky/music/gmmusic.cpp
+++ b/engines/sky/music/gmmusic.cpp
@@ -44,6 +44,7 @@ GmMusic::GmMusic(MidiDriver *pMidiDrv, Disk *pDisk) : MusicBase(pDisk) {
error("Can't open midi device. Errorcode: %d", midiRes);
_timerCount = 0;
_midiDrv->setTimerCallback(this, passTimerFunc);
+ _midiDrv->sendGMReset();
}
GmMusic::~GmMusic() {
diff --git a/engines/sky/music/mt32music.cpp b/engines/sky/music/mt32music.cpp
index ce2a29dad8..bdefa66709 100644
--- a/engines/sky/music/mt32music.cpp
+++ b/engines/sky/music/mt32music.cpp
@@ -44,6 +44,7 @@ MT32Music::MT32Music(MidiDriver *pMidiDrv, Disk *pDisk) : MusicBase(pDisk) {
error("Can't open midi device. Errorcode: %d",midiRes);
_timerCount = 0;
_midiDrv->setTimerCallback(this, passTimerFunc);
+ _midiDrv->sendMT32Reset();
}
MT32Music::~MT32Music() {
diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
index edf96f8e8c..30f67bf5ef 100644
--- a/engines/sky/sky.cpp
+++ b/engines/sky/sky.cpp
@@ -337,7 +337,7 @@ Common::Error SkyEngine::init() {
}
if (!_skyDisk->fileExists(60600 + SkyEngine::_systemVars.language * 8)) {
- warning("The language you selected does not exist in your BASS version.");
+ warning("The language you selected does not exist in your BASS version");
if (_skyDisk->fileExists(60600))
SkyEngine::_systemVars.language = SKY_ENGLISH; // default to GB english if it exists..
else if (_skyDisk->fileExists(60600 + SKY_USA * 8))
diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp
index 35d8e14f27..ef54167d41 100644
--- a/engines/sword1/logic.cpp
+++ b/engines/sword1/logic.cpp
@@ -1017,7 +1017,7 @@ int Logic::fnNewScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, in
int Logic::fnSubScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
cpt->o_tree.o_script_level++;
if (cpt->o_tree.o_script_level == TOTAL_script_levels)
- error("Compact %d: script level exceeded in fnSubScript.", id);
+ error("Compact %d: script level exceeded in fnSubScript", id);
cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = script;
cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = script;
return SCRIPT_STOP;
@@ -1605,7 +1605,7 @@ int Logic::fnStopMusic(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d
}
int Logic::fnInnerSpace(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
- error("fnInnerSpace() not working.");
+ error("fnInnerSpace() not working");
return SCRIPT_STOP; // for compilers that don't support NORETURN
}
diff --git a/engines/sword1/router.cpp b/engines/sword1/router.cpp
index 0da1cef6fe..84cc81cd0e 100644
--- a/engines/sword1/router.cpp
+++ b/engines/sword1/router.cpp
@@ -845,7 +845,7 @@ void Router::slidyWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 104;//turning left
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
}
if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) {
// turn at the end of the current walk
@@ -853,7 +853,7 @@ void Router::slidyWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 200; //was 60 now 116
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
}
lastDir = currentDir;
}
@@ -1272,7 +1272,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 104;//turning left
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
}
if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6))) {
// turn at the end of the current walk
@@ -1280,7 +1280,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 200; //was 60 now 116
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
}
}
// all turns checked
@@ -1305,7 +1305,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 278;//stopping right
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
walkAnim[stepCount].frame = 308;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
@@ -1316,7 +1316,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 279;//stopping right
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
walkAnim[stepCount].frame = 315;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
@@ -1332,7 +1332,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 244;//stopping left
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
walkAnim[stepCount].frame = 322;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
@@ -1343,7 +1343,7 @@ int32 Router::solidWalkAnimator(WalkData *walkAnim) {
do {
walkAnim[frame].frame += 245;//stopping left
frame += 1;
- } while (frame < lastCount );
+ } while (frame < lastCount);
walkAnim[stepCount].frame = 329;
walkAnim[stepCount].step = 7;
walkAnim[stepCount].dir = currentDir;
@@ -2097,9 +2097,9 @@ int whatTarget(int32 startX, int32 startY, int32 destX, int32 destY) {
int signY = (deltaY > 0);
int slope;
- if ((ABS(deltaY) * DIAGONALX ) < (ABS(deltaX) * DIAGONALY / 2))
+ if ((ABS(deltaY) * DIAGONALX) < (ABS(deltaX) * DIAGONALY / 2))
slope = 0;// its flat
- else if ((ABS(deltaY) * DIAGONALX / 2) > (ABS(deltaX) * DIAGONALY ) )
+ else if ((ABS(deltaY) * DIAGONALX / 2) > (ABS(deltaX) * DIAGONALY))
slope = 2;// its vertical
else
slope = 1;// its diagonal
diff --git a/engines/sword1/screen.cpp b/engines/sword1/screen.cpp
index 024b681acb..b07acf9a0b 100644
--- a/engines/sword1/screen.cpp
+++ b/engines/sword1/screen.cpp
@@ -366,7 +366,7 @@ void Screen::draw() {
if (_currentScreen == 54) {
// rm54 has a BACKGROUND parallax layer in parallax[0]
- if (_parallax[0] && !SwordEngine::isPsx() ) //Avoid drawing this parallax on PSX edition, it gets occluded by background
+ if (_parallax[0] && !SwordEngine::isPsx()) //Avoid drawing this parallax on PSX edition, it gets occluded by background
renderParallax(_parallax[0]);
uint8 *src = _layerBlocks[0];
uint8 *dest = _screenBuf;
@@ -539,7 +539,7 @@ void Screen::processImage(uint32 id) {
|| (compact->o_resource == LVSFLY) || (!(compact->o_resource == GEORGE_MEGA) && (sprSizeX < 260))))
drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch);
else if (((sprSizeX >= 260) && (sprSizeX < 450)) || ((compact->o_resource == GMWRITH) && (sprSizeX < 515)) // a psx shrinked sprite (1/2 width)
- || ((compact->o_resource == GMPOWER) && (sprSizeX < 515)) ) // some needs to be hardcoded, headers don't give useful infos
+ || ((compact->o_resource == GMPOWER) && (sprSizeX < 515))) // some needs to be hardcoded, headers don't give useful infos
drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 2, sprSizeY, sprPitch / 2);
else if (sprSizeX >= 450) // A PSX double shrinked sprite (1/3 width)
drawPsxFullShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 3, sprSizeY, sprPitch / 3);
@@ -1187,7 +1187,7 @@ void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, u
gridW *= 2; // and masking problems when sprites are stretched in width
uint16 bottomSprPos = (*pSprY + (*pSprHeight) * 2); //Position of bottom line of sprite
- if ( bottomSprPos > _scrnSizeY ) { //Check that resized psx sprite isn't drawn outside of screen boundaries
+ if (bottomSprPos > _scrnSizeY) { //Check that resized psx sprite isn't drawn outside of screen boundaries
uint16 outScreen = bottomSprPos - _scrnSizeY;
*pSprHeight -= (outScreen % 2) ? (outScreen + 1) / 2 : outScreen / 2;
}
diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp
index da9a83cdff..55600cfb63 100644
--- a/engines/sword1/sound.cpp
+++ b/engines/sword1/sound.cpp
@@ -535,7 +535,7 @@ void Sound::calcWaveVolume(int16 *data, uint32 length) {
_waveVolPos = 0;
for (uint32 blkCnt = 1; blkCnt < length / 918; blkCnt++) {
if (blkCnt >= WAVE_VOL_TAB_LENGTH) {
- warning("Wave vol tab too small.");
+ warning("Wave vol tab too small");
return;
}
int32 average = 0;
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
index 26bb1d959b..0d4f5b7445 100644
--- a/engines/sword1/sword1.cpp
+++ b/engines/sword1/sword1.cpp
@@ -214,12 +214,20 @@ void SwordEngine::syncSoundSettings() {
sfxVolL = 255;
}
- _music->setVolume(musicVolL, musicVolR);
- _sound->setSpeechVol(speechVolL, speechVolR);
- _sound->setSfxVol(sfxVolL, sfxVolR);
+ bool mute = ConfMan.getBool("mute");
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+ if (mute) {
+ _music->setVolume(0, 0);
+ _sound->setSpeechVol(0, 0);
+ _sound->setSfxVol(0, 0);
+ } else {
+ _music->setVolume(musicVolL, musicVolR);
+ _sound->setSpeechVol(speechVolL, speechVolR);
+ _sound->setSfxVol(sfxVolL, sfxVolR);
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume"));
}
void SwordEngine::flagsToBool(bool *dest, uint8 flags) {
diff --git a/engines/sword1/text.cpp b/engines/sword1/text.cpp
index e8a20ec2b7..ef3e07fe74 100644
--- a/engines/sword1/text.cpp
+++ b/engines/sword1/text.cpp
@@ -49,7 +49,7 @@ Text::Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion) {
_fontId = (czechVersion) ? CZECH_GAME_FONT : GAME_FONT;
_font = (uint8*)_resMan->openFetchRes(_fontId);
- _joinWidth = charWidth( SPACE ) - 2 * OVERLAP;
+ _joinWidth = charWidth(SPACE) - 2 * OVERLAP;
_charHeight = _resMan->getUint16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
for (int i = 0; i < MAX_TEXT_OBS; i++)
_textBlocks[i] = NULL;
@@ -145,12 +145,12 @@ uint16 Text::analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *line) {
// (with a separating space character - also overlapped)
uint16 spaceNeeded = _joinWidth + wordWidth;
- if (line[lineNo].width + spaceNeeded <= maxWidth ) {
+ if (line[lineNo].width + spaceNeeded <= maxWidth) {
line[lineNo].width += spaceNeeded;
line[lineNo].length += 1 + wordLength; // NB. space+word characters
} else { // put word (without separating SPACE) at start of next line
lineNo++;
- assert( lineNo < MAX_LINES );
+ assert(lineNo < MAX_LINES);
line[lineNo].width = wordWidth;
line[lineNo].length = wordLength;
}
diff --git a/engines/sword2/mouse.cpp b/engines/sword2/mouse.cpp
index 273ad1ec60..4dbb38eaca 100644
--- a/engines/sword2/mouse.cpp
+++ b/engines/sword2/mouse.cpp
@@ -341,7 +341,7 @@ void Mouse::systemMenuMouse() {
if ((icon_list[hit] == OPTIONS_ICON || icon_list[hit] == QUIT_ICON
|| icon_list[hit] == SAVE_ICON || icon_list[hit] == RESTORE_ICON
- || icon_list[hit] == RESTART_ICON ) && Sword2Engine::isPsx() )
+ || icon_list[hit] == RESTART_ICON) && Sword2Engine::isPsx())
return;
// No save when dead
diff --git a/engines/sword2/screen.cpp b/engines/sword2/screen.cpp
index fb2e108b2f..1e45c0fc1f 100644
--- a/engines/sword2/screen.cpp
+++ b/engines/sword2/screen.cpp
@@ -966,11 +966,15 @@ void Screen::rollCredits() {
if (Sword2Engine::isPsx()) {
if (!f.open("credits.txt")) {
warning("Can't find credits.txt");
+
+ free(logoData);
return;
}
} else {
if (!f.open("credits.clu")) {
warning("Can't find credits.clu");
+
+ free(logoData);
return;
}
}
diff --git a/engines/sword2/sprite.cpp b/engines/sword2/sprite.cpp
index 5a45c19ab6..7d45f8df4e 100644
--- a/engines/sword2/sprite.cpp
+++ b/engines/sword2/sprite.cpp
@@ -528,8 +528,11 @@ int32 Screen::drawSprite(SpriteInfo *s) {
decompData = decompressHIF(s->data, tempBuf);
// Check that we correctly decompressed data
- if (!decompData)
+ if (!decompData) {
+ free(tempBuf);
+
return RDERR_DECOMPRESSION;
+ }
s->w = (decompData / (s->h / 2)) * 2;
byte *tempBuf2 = (byte *)malloc(s->w * s->h * 10);
@@ -571,8 +574,11 @@ int32 Screen::drawSprite(SpriteInfo *s) {
uint32 decompData = decompressHIF(s->data, tempBuf);
// Check that we correctly decompressed data
- if (!decompData)
+ if (!decompData) {
+ free(tempBuf);
+
return RDERR_DECOMPRESSION;
+ }
s->w = (decompData / (s->h / 2));
sprite = (byte *)malloc(s->w * s->h);
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index 3cdab2bd2b..b3b688771a 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -326,18 +326,24 @@ void Sword2Engine::registerDefaultSettings() {
}
void Sword2Engine::syncSoundSettings() {
- _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"));
+ bool mute = ConfMan.getBool("mute");
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, mute ? 0 : ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : ConfMan.getInt("speech_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : 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);
+ if (ConfMan.hasKey("mute")) {
+ ConfMan.setBool("music_mute", ConfMan.getBool("mute"));
+ ConfMan.setBool("speech_mute", ConfMan.getBool("mute"));
+ ConfMan.setBool("sfx_mute", ConfMan.getBool("mute"));
+
+ if (!mute) // it is false
+ // So remove it in order to let individual volumes work
+ ConfMan.removeKey("mute", ConfMan.getActiveDomainName());
}
_sound->muteMusic(ConfMan.getBool("music_mute"));
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
new file mode 100644
index 0000000000..e210f4d27c
--- /dev/null
+++ b/engines/sword25/detection.cpp
@@ -0,0 +1,130 @@
+/* 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 "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+
+#include "sword25/sword25.h"
+
+namespace Sword25 {
+uint32 Sword25Engine::getGameFlags() const { return _gameDescription->flags; }
+}
+
+static const PlainGameDescriptor Sword25Game[] = {
+ {"sword25", "Broken Sword 2.5"},
+ {0, 0}
+};
+
+namespace Sword25 {
+
+// TODO: Need to decide whether we're going to implement code to detect all the various languages allowed,
+// both by the core data package, as well as the extra languages added by the patch file; also, I don't
+// think that all the languages supported by the game currently have constants in ScummVM
+static const ADGameDescription gameDescriptions[] = {
+ {
+ "sword25",
+ "",
+ AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588),
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ {
+ "sword25",
+ "Extracted",
+ {{"_includes.lua", 0, 0, -1},
+ {"boot.lua", 0, 0, -1},
+ {"kernel.lua", 0, 0, -1},
+ AD_LISTEND},
+ Common::EN_ANY,
+ Common::kPlatformUnknown,
+ GF_EXTRACTED,
+ Common::GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+} // end of namespace Sword25
+
+static const char *directoryGlobs[] = {
+ "system", // Used by extracted dats
+ 0
+};
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)Sword25::gameDescriptions,
+ // Size of that superset structure
+ sizeof(ADGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ Sword25Game,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ NULL,
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
+};
+
+class Sword25MetaEngine : public AdvancedMetaEngine {
+public:
+ Sword25MetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ virtual const char *getName() const {
+ return "The Broken Sword 2.5 Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Broken Sword 2.5 (C) Malte Thiesen, Daniel Queteschiner and Michael Elsdorfer";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+};
+
+bool Sword25MetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new Sword25::Sword25Engine(syst, desc);
+ }
+ return desc != 0;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(SWORD25)
+ REGISTER_PLUGIN_DYNAMIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(SWORD25, PLUGIN_TYPE_ENGINE, Sword25MetaEngine);
+#endif
+
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
new file mode 100644
index 0000000000..f6757b9a8e
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -0,0 +1,154 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/fmv/movieplayer.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/sfx/soundengine.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "MOVIEPLAYER"
+
+#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON != 1.0 */
+
+Service *OggTheora_CreateObject(Kernel *pKernel) {
+ return new MoviePlayer(pKernel);
+}
+
+MoviePlayer::MoviePlayer(Kernel *pKernel) : Service(pKernel), _decoder(g_system->getMixer()) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+MoviePlayer::~MoviePlayer() {
+ _decoder.close();
+}
+
+bool MoviePlayer::loadMovie(const Common::String &filename, uint z) {
+ // Get the file and load it into the decoder
+ Common::SeekableReadStream *in = Kernel::GetInstance()->GetPackage()->getStream(filename);
+ _decoder.load(in);
+
+ // Ausgabebitmap erstellen
+ GraphicEngine *pGfx = Kernel::GetInstance()->GetGfx();
+ _outputBitmap = pGfx->GetMainPanel()->addDynamicBitmap(_decoder.getWidth(), _decoder.getHeight());
+ if (!_outputBitmap.isValid()) {
+ BS_LOG_ERRORLN("Output bitmap for movie playback could not be created.");
+ return false;
+ }
+
+ // Skalierung des Ausgabebitmaps berechnen, so dass es möglichst viel Bildschirmfläche einnimmt.
+ float screenToVideoWidth = (float)pGfx->GetDisplayWidth() / (float)_outputBitmap->getWidth();
+ float screenToVideoHeight = (float)pGfx->GetDisplayHeight() / (float)_outputBitmap->getHeight();
+ float scaleFactor = MIN(screenToVideoWidth, screenToVideoHeight);
+
+ if (abs((int)(scaleFactor - 1.0f)) < FLT_EPSILON)
+ scaleFactor = 1.0f;
+
+ _outputBitmap->setScaleFactor(scaleFactor);
+
+ // Z-Wert setzen
+ _outputBitmap->setZ(z);
+
+ // Ausgabebitmap auf dem Bildschirm zentrieren
+ _outputBitmap->setX((pGfx->GetDisplayWidth() - _outputBitmap->getWidth()) / 2);
+ _outputBitmap->setY((pGfx->GetDisplayHeight() - _outputBitmap->getHeight()) / 2);
+
+ return true;
+}
+
+bool MoviePlayer::unloadMovie() {
+ _decoder.close();
+ _outputBitmap.erase();
+
+ return true;
+}
+
+bool MoviePlayer::play() {
+ _decoder.pauseVideo(false);
+ return true;
+}
+
+bool MoviePlayer::pause() {
+ _decoder.pauseVideo(true);
+ return true;
+}
+
+void MoviePlayer::update() {
+ if (_decoder.isVideoLoaded()) {
+ Graphics::Surface *s = _decoder.decodeNextFrame();
+
+ // Transfer the next frame
+ assert(s->bytesPerPixel == 4);
+ byte *frameData = (byte *)s->getBasePtr(0, 0);
+ _outputBitmap->setContent(frameData, s->pitch * s->h, 0, s->pitch);
+ }
+}
+
+bool MoviePlayer::isMovieLoaded() {
+ return _decoder.isVideoLoaded();
+}
+
+bool MoviePlayer::isPaused() {
+ return _decoder.isPaused();
+}
+
+float MoviePlayer::getScaleFactor() {
+ if (_decoder.isVideoLoaded())
+ return _outputBitmap->getScaleFactorX();
+ else
+ return 0;
+}
+
+void MoviePlayer::setScaleFactor(float scaleFactor) {
+ if (_decoder.isVideoLoaded()) {
+ _outputBitmap->setScaleFactor(scaleFactor);
+
+ // Ausgabebitmap auf dem Bildschirm zentrieren
+ GraphicEngine *gfxPtr = Kernel::GetInstance()->GetGfx();
+ _outputBitmap->setX((gfxPtr->GetDisplayWidth() - _outputBitmap->getWidth()) / 2);
+ _outputBitmap->setY((gfxPtr->GetDisplayHeight() - _outputBitmap->getHeight()) / 2);
+ }
+}
+
+double MoviePlayer::getTime() {
+ return _decoder.getElapsedTime() / 1000.0;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
new file mode 100644
index 0000000000..96beb648c0
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer.h
@@ -0,0 +1,145 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_MOVIEPLAYER_H
+#define SWORD25_MOVIEPLAYER_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/fmv/theora_decoder.h"
+#include "sword25/gfx/bitmap.h"
+
+namespace Sword25 {
+
+class MoviePlayer : public Service {
+public:
+ // -----------------------------------------------------------------------------
+ // Constructor / Destructor
+ // -----------------------------------------------------------------------------
+
+ MoviePlayer(Kernel *pKernel);
+ ~MoviePlayer();
+
+ // -----------------------------------------------------------------------------
+ // Player interface must be implemented by a Movie Player
+ // -----------------------------------------------------------------------------
+
+ /**
+ * Loads a movie file
+ *
+ * This method loads a movie file and prepares it for playback.
+ * There can be oly one movie file loaded at a time. If you already have loaded a
+ * movie file, it will be unloaded and, if necessary, stopped playing.
+ * @param Filename The filename of the movie file to be loaded
+ * @param Z Z indicates the position of the film on the main graphics layer
+ * @return Returns false if an error occured while loading, otherwise true.
+ */
+ bool loadMovie(const Common::String &filename, uint z);
+
+ /**
+ * Unloads the currently loaded movie file.
+ * @return Returns false if an error occurred while unloading, otherwise true.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ bool unloadMovie();
+
+ /**
+ * Plays the loaded movie.
+ *
+ * The film will be keeping the aspect ratio of the screen.
+ * If the film was previously paused with Pause(), then the film will resume playing.
+ * @return Returns false if an error occurred while starting, otherwise true.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ bool play();
+
+ /**
+ * Pauses movie playback.
+ *
+ * A paused movie can later be resumed by calling the Play() method again.
+ * @return Returns false if an error occurred while pausing, otherwise true.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ bool pause();
+
+ /**
+ * This function must be called once per frame.
+ */
+ void update();
+
+ /**
+ * Returns whether a film is loaded for playback.
+ */
+ bool isMovieLoaded();
+
+ /**
+ * Returns whether the movie playback is paused.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ bool isPaused();
+
+ /**
+ * Returns the scaling factor for the loaded film.
+ *
+ * When a movie is loaded, the scaling factor is automatically selected so that the film
+ * takes the maximum screen space, without the film being distorted.
+ * @return Returns the scaling factor of the film.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ float getScaleFactor();
+
+ /**
+ * Sets the factor by which the loaded film is to be scaled.
+ * @param ScaleFactor The desired scale factor.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ void setScaleFactor(float scaleFactor);
+
+ /**
+ * Returns the current playing position in seconds.
+ * @remark This method can only be called when IsMovieLoaded() returns true.
+ */
+ double getTime();
+
+private:
+ bool registerScriptBindings();
+
+ TheoraDecoder _decoder;
+
+ RenderObjectPtr<Bitmap> _outputBitmap;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/fmv/movieplayer_script.cpp b/engines/sword25/fmv/movieplayer_script.cpp
new file mode 100644
index 0000000000..13bb149672
--- /dev/null
+++ b/engines/sword25/fmv/movieplayer_script.cpp
@@ -0,0 +1,163 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+
+#include "sword25/fmv/movieplayer.h"
+
+namespace Sword25 {
+
+int loadMovie(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->loadMovie(luaL_checkstring(L, 1), lua_gettop(L) == 2 ? static_cast<uint>(luaL_checknumber(L, 2)) : 10));
+
+ return 1;
+}
+
+int unloadMovie(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->unloadMovie());
+
+ return 1;
+}
+
+int play(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->play());
+
+ return 1;
+}
+
+int pause(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->pause());
+
+ return 1;
+}
+
+int update(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ FMVPtr->update();
+
+ return 0;
+}
+
+int isMovieLoaded(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->isMovieLoaded());
+
+ return 1;
+}
+
+int isPaused(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushbooleancpp(L, FMVPtr->isPaused());
+
+ return 1;
+}
+
+int getScaleFactor(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushnumber(L, FMVPtr->getScaleFactor());
+
+ return 1;
+}
+
+int setScaleFactor(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ FMVPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+int getTime(lua_State *L) {
+ MoviePlayer *FMVPtr = Kernel::GetInstance()->GetFMV();
+ BS_ASSERT(FMVPtr);
+
+ lua_pushnumber(L, FMVPtr->getTime());
+
+ return 1;
+}
+
+const char *LIBRARY_NAME = "Movieplayer";
+
+const luaL_reg LIBRARY_FUNCTIONS[] = {
+ { "LoadMovie", loadMovie },
+ { "UnloadMovie", unloadMovie },
+ { "Play", play },
+ { "Pause", pause },
+ { "Update", update },
+ { "IsMovieLoaded", isMovieLoaded },
+ { "IsPaused", isPaused },
+ { "GetScaleFactor", getScaleFactor },
+ { "SetScaleFactor", setScaleFactor },
+ { "GetTime", getTime },
+ { 0, 0 }
+};
+
+bool MoviePlayer::registerScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, LIBRARY_NAME, LIBRARY_FUNCTIONS)) return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp
new file mode 100644
index 0000000000..9b1951828e
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.cpp
@@ -0,0 +1,492 @@
+/* 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$
+ *
+ */
+
+/*
+ * Source is based on the player example from libvorbis package
+ *
+ * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE.
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.
+ *
+ * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009
+ * by the Xiph.Org Foundation and contributors http://www.xiph.org/
+ *
+ */
+
+#include "sword25/fmv/theora_decoder.h"
+#include "sword25/fmv/yuvtorgba.h"
+#include "common/system.h"
+#include "sound/decoders/raw.h"
+
+namespace Sword25 {
+
+#define AUDIOFD_FRAGSIZE 10240
+
+static double rint(double v) {
+ return floor(v + 0.5);
+}
+
+TheoraDecoder::TheoraDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) {
+ _fileStream = 0;
+ _surface = 0;
+
+ _theoraPacket = 0;
+ _vorbisPacket = 0;
+ _theoraDecode = 0;
+ _theoraSetup = 0;
+ _stateFlag = false;
+
+ _soundType = soundType;
+ _audStream = 0;
+ _audHandle = new Audio::SoundHandle();
+
+ ogg_sync_init(&_oggSync);
+
+ _curFrame = 0;
+ _audiobuf = (ogg_int16_t *)calloc(AUDIOFD_FRAGSIZE, sizeof(ogg_int16_t));
+
+ reset();
+}
+
+TheoraDecoder::~TheoraDecoder() {
+ close();
+ delete _fileStream;
+ delete _audHandle;
+ free(_audiobuf);
+}
+
+void TheoraDecoder::queuePage(ogg_page *page) {
+ if (_theoraPacket)
+ ogg_stream_pagein(&_theoraOut, page);
+
+ if (_vorbisPacket)
+ ogg_stream_pagein(&_vorbisOut, page);
+}
+
+int TheoraDecoder::bufferData() {
+ char *buffer = ogg_sync_buffer(&_oggSync, 4096);
+ int bytes = _fileStream->read(buffer, 4096);
+
+ ogg_sync_wrote(&_oggSync, bytes);
+
+ return bytes;
+}
+
+bool TheoraDecoder::load(Common::SeekableReadStream *stream) {
+ close();
+
+ _fileStream = stream;
+
+ // start up Ogg stream synchronization layer
+ ogg_sync_init(&_oggSync);
+
+ // init supporting Vorbis structures needed in header parsing
+ vorbis_info_init(&_vorbisInfo);
+ vorbis_comment_init(&_vorbisComment);
+
+ // init supporting Theora structures needed in header parsing
+ th_comment_init(&_theoraComment);
+ th_info_init(&_theoraInfo);
+
+ // Ogg file open; parse the headers
+ // Only interested in Vorbis/Theora streams
+ while (!_stateFlag) {
+ int ret = bufferData();
+
+ if (ret == 0)
+ break;
+
+ while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ ogg_stream_state test;
+
+ // is this a mandated initial header? If not, stop parsing
+ if (!ogg_page_bos(&_oggPage)) {
+ // don't leak the page; get it into the appropriate stream
+ queuePage(&_oggPage);
+ _stateFlag = true;
+ break;
+ }
+
+ ogg_stream_init(&test, ogg_page_serialno(&_oggPage));
+ ogg_stream_pagein(&test, &_oggPage);
+ ogg_stream_packetout(&test, &_oggPacket);
+
+ // identify the codec: try theora
+ if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) {
+ // it is theora
+ memcpy(&_theoraOut, &test, sizeof(test));
+ _theoraPacket = 1;
+ } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) {
+ // it is vorbis
+ memcpy(&_vorbisOut, &test, sizeof(test));
+ _vorbisPacket = 1;
+ } else {
+ // whatever it is, we don't care about it
+ ogg_stream_clear(&test);
+ }
+ }
+ // fall through to non-bos page parsing
+ }
+
+ // we're expecting more header packets.
+ while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) {
+ int ret;
+
+ // look for further theora headers
+ while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) {
+ if (ret < 0)
+ error("Error parsing Theora stream headers; corrupt stream?");
+
+ if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket))
+ error("Error parsing Theora stream headers; corrupt stream?");
+
+ _theoraPacket++;
+ }
+
+ // look for more vorbis header packets
+ while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) {
+ if (ret < 0)
+ error("Error parsing Vorbis stream headers; corrupt stream?");
+
+ if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket))
+ error("Error parsing Vorbis stream headers; corrupt stream?");
+
+ _vorbisPacket++;
+
+ if (_vorbisPacket == 3)
+ break;
+ }
+
+ // The header pages/packets will arrive before anything else we
+ // care about, or the stream is not obeying spec
+
+ if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ queuePage(&_oggPage); // demux into the appropriate stream
+ } else {
+ ret = bufferData(); // someone needs more data
+
+ if (ret == 0)
+ error("End of file while searching for codec headers.");
+ }
+ }
+
+ // and now we have it all. initialize decoders
+ if (_theoraPacket) {
+ _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup);
+ debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps",
+ _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height,
+ (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator);
+
+ switch (_theoraInfo.pixel_fmt) {
+ case TH_PF_420:
+ debug(1, " 4:2:0 video");
+ break;
+ case TH_PF_422:
+ debug(1, " 4:2:2 video");
+ break;
+ case TH_PF_444:
+ debug(1, " 4:4:4 video");
+ break;
+ case TH_PF_RSVD:
+ default:
+ debug(1, " video\n (UNKNOWN Chroma sampling!)");
+ break;
+ }
+
+ if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height)
+ debug(1, " Frame content is %dx%d with offset (%d,%d).",
+ _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y);
+
+ switch (_theoraInfo.colorspace){
+ case TH_CS_UNSPECIFIED:
+ /* nothing to report */
+ break;;
+ case TH_CS_ITU_REC_470M:
+ debug(1, " encoder specified ITU Rec 470M (NTSC) color.");
+ break;
+ case TH_CS_ITU_REC_470BG:
+ debug(1, " encoder specified ITU Rec 470BG (PAL) color.");
+ break;
+ default:
+ debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace);
+ break;
+ }
+
+ debug(1, "Encoded by %s", _theoraComment.vendor);
+ if (_theoraComment.comments) {
+ debug(1, "theora comment header:");
+ for (int i = 0; i < _theoraComment.comments; i++) {
+ if (_theoraComment.user_comments[i]) {
+ int len = _theoraComment.comment_lengths[i];
+ char *value = (char *)malloc(len + 1);
+ memcpy(value, _theoraComment.user_comments[i], len);
+ value[len] = '\0';
+ debug(1, "\t%s", value);
+ free(value);
+ }
+ }
+ }
+
+ th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax));
+ _ppLevel = _ppLevelMax;
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
+ _ppInc = 0;
+ } else {
+ // tear down the partial theora setup
+ th_info_clear(&_theoraInfo);
+ th_comment_clear(&_theoraComment);
+ }
+
+ th_setup_free(_theoraSetup);
+ _theoraSetup = 0;
+
+ if (_vorbisPacket) {
+ vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo);
+ vorbis_block_init(&_vorbisDSP, &_vorbisBlock);
+ debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.",
+ _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate);
+ } else {
+ // tear down the partial vorbis setup
+ vorbis_info_clear(&_vorbisInfo);
+ vorbis_comment_clear(&_vorbisComment);
+ }
+
+ // open audio
+ if (_vorbisPacket) {
+ _audStream = createAudioStream();
+ if (_audStream && _mixer)
+ _mixer->playStream(_soundType, _audHandle, _audStream);
+ }
+
+ _surface = new Graphics::Surface();
+
+ _surface->create(_theoraInfo.frame_width, _theoraInfo.frame_height, 4);
+
+ return true;
+}
+
+void TheoraDecoder::close() {
+ if (_vorbisPacket) {
+ ogg_stream_clear(&_vorbisOut);
+ vorbis_block_clear(&_vorbisBlock);
+ vorbis_dsp_clear(&_vorbisDSP);
+ vorbis_comment_clear(&_vorbisComment);
+ vorbis_info_clear(&_vorbisInfo);
+
+ if (_mixer)
+ _mixer->stopHandle(*_audHandle);
+ _audStream = 0;
+ _vorbisPacket = false;
+ }
+ if (_theoraPacket) {
+ ogg_stream_clear(&_theoraOut);
+ th_decode_free(_theoraDecode);
+ th_comment_clear(&_theoraComment);
+ th_info_clear(&_theoraInfo);
+ _theoraDecode = 0;
+ _theoraPacket = false;
+ }
+
+ if (!_fileStream)
+ return;
+
+ ogg_sync_clear(&_oggSync);
+
+ delete _fileStream;
+ _fileStream = 0;
+
+ _surface->free();
+ delete _surface;
+ _surface = 0;
+
+ reset();
+}
+
+Graphics::Surface *TheoraDecoder::decodeNextFrame() {
+ int i, j;
+
+// _stateFlag = false; // playback has not begun
+
+ // we want a video and audio frame ready to go at all times. If
+ // we have to buffer incoming, buffer the compressed data (ie, let
+ // ogg do the buffering)
+ while (_vorbisPacket && !_audiobufReady) {
+ int ret;
+ float **pcm;
+
+ // if there's pending, decoded audio, grab it
+ if ((ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm)) > 0) {
+ int count = _audiobufFill / 2;
+ int maxsamples = (AUDIOFD_FRAGSIZE - _audiobufFill) / 2 / _vorbisInfo.channels;
+ for (i = 0; i < ret && i < maxsamples; i++)
+ for (j = 0; j < _vorbisInfo.channels; j++) {
+ int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32768);
+ _audiobuf[count++] = val;
+ }
+
+ vorbis_synthesis_read(&_vorbisDSP, i);
+ _audiobufFill += i * _vorbisInfo.channels * 2;
+
+ if (_audiobufFill == AUDIOFD_FRAGSIZE)
+ _audiobufReady = true;
+
+ if (_vorbisDSP.granulepos >= 0)
+ _audiobufGranulePos = _vorbisDSP.granulepos - ret + i;
+ else
+ _audiobufGranulePos += i;
+ } else {
+
+ // no pending audio; is there a pending packet to decode?
+ if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) {
+ if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success!
+ vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock);
+ } else // we need more data; break out to suck in another page
+ break;
+ }
+ }
+
+ while (_theoraPacket && !_videobufReady) {
+ // theora is one in, one out...
+ if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) {
+
+ if (_ppInc) {
+ _ppLevel += _ppInc;
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel));
+ _ppInc = 0;
+ }
+ // HACK: This should be set after a seek or a gap, but we might not have
+ // a granulepos for the first packet (we only have them for the last
+ // packet on a page), so we just set it as often as we get it.
+ // To do this right, we should back-track from the last packet on the
+ // page and compute the correct granulepos for the first packet after
+ // a seek or a gap.
+ if (_oggPacket.granulepos >= 0) {
+ th_decode_ctl(_theoraDecode, TH_DECCTL_SET_GRANPOS, &_oggPacket.granulepos, sizeof(_oggPacket.granulepos));
+ }
+ if (th_decode_packetin(_theoraDecode, &_oggPacket, &_videobufGranulePos) == 0) {
+ _videobufTime = th_granule_time(_theoraDecode, _videobufGranulePos);
+ _curFrame++;
+
+ _videobufReady = true;
+ }
+ } else
+ break;
+ }
+
+ if (!_videobufReady && !_audiobufReady && _fileStream->eos()) {
+ close();
+ return _surface;
+ }
+
+ if (!_videobufReady || !_audiobufReady) {
+ // no data yet for somebody. Grab another page
+ bufferData();
+ while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) {
+ queuePage(&_oggPage);
+ }
+ }
+
+ // If playback has begun, top audio buffer off immediately.
+/* FIXME: This is currently crashing
+ if (_stateFlag) {
+ _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+ }
+*/
+
+ // are we at or past time for this video frame?
+ if (_stateFlag && _videobufReady) {
+ th_ycbcr_buffer yuv;
+
+ th_decode_ycbcr_out(_theoraDecode, yuv);
+
+ // Convert YUV data to RGB data
+ YUVtoBGRA::translate(yuv, _theoraInfo, (byte *)_surface->getBasePtr(0, 0), _surface->pitch * _surface->h);
+
+ switch (_theoraInfo.pixel_fmt) {
+ case TH_PF_420:
+ break;
+ case TH_PF_422:
+ break;
+ case TH_PF_444:
+ break;
+ default:
+ break;
+ }
+
+ _videobufReady = false;
+ }
+
+ // if our buffers either don't exist or are ready to go,
+ // we can begin playback
+ if ((!_theoraPacket || _videobufReady) &&
+ (!_vorbisPacket || _audiobufReady))
+ _stateFlag = true;
+
+ // same if we've run out of input
+ if (_fileStream->eos())
+ _stateFlag = true;
+
+ return _surface;
+}
+
+void TheoraDecoder::reset() {
+ VideoDecoder::reset();
+
+ if (_fileStream)
+ _fileStream->seek(0);
+
+ _videobufReady = false;
+ _videobufGranulePos = -1;
+ _videobufTime = 0;
+
+ _audiobufFill = 0;
+ _audiobufReady = false;
+ _audiobufGranulePos = 0;
+
+ _curFrame = 0;
+
+ _theoraPacket = 0;
+ _vorbisPacket = 0;
+ _stateFlag = false;
+}
+
+bool TheoraDecoder::endOfVideo() const {
+ return !isVideoLoaded();
+}
+
+
+uint32 TheoraDecoder::getElapsedTime() const {
+ if (_audStream && _mixer)
+ return _mixer->getSoundElapsedTime(*_audHandle);
+
+ return VideoDecoder::getElapsedTime();
+}
+
+Audio::QueuingAudioStream *TheoraDecoder::createAudioStream() {
+ return Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h
new file mode 100644
index 0000000000..12d8035c0a
--- /dev/null
+++ b/engines/sword25/fmv/theora_decoder.h
@@ -0,0 +1,152 @@
+/* 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 SWORD25_THEORADECODER_H
+#define SWORD25_THEORADECODER_H
+
+#include "graphics/video/video_decoder.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+#include <theora/theoradec.h>
+#include <vorbis/codec.h>
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Sword25 {
+
+/**
+ *
+ * Decoder for Theora videos.
+ * Video decoder used in engines:
+ * - sword25
+ */
+class TheoraDecoder : public Graphics::FixedRateVideoDecoder {
+public:
+ TheoraDecoder(Audio::Mixer *mixer = 0, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
+ virtual ~TheoraDecoder();
+
+ /**
+ * Load a video file
+ * @param stream the stream to load
+ */
+ bool load(Common::SeekableReadStream *stream);
+ void close();
+ void reset();
+
+ /**
+ * Decode the next frame and return the frame's surface
+ * @note the return surface should *not* be freed
+ * @note this may return 0, in which case the last frame should be kept on screen
+ */
+ Graphics::Surface *decodeNextFrame();
+
+ bool isVideoLoaded() const {
+ return _fileStream != 0;
+ }
+ bool isPaused() const {
+ return (VideoDecoder::isPaused() || !isVideoLoaded());
+ }
+
+ uint16 getWidth() const {
+ return _surface->w;
+ }
+ uint16 getHeight() const {
+ return _surface->h;
+ }
+ uint32 getFrameCount() const {
+ // It is not possible to get frame count easily
+ // I.e. seeking is required
+ assert(0);
+ return 0;
+ }
+ Graphics::PixelFormat getPixelFormat() const {
+ return Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
+ }
+
+ uint32 getElapsedTime() const;
+
+ bool endOfVideo() const;
+
+protected:
+ Common::Rational getFrameRate() const {
+ return _frameRate;
+ }
+
+private:
+ void queuePage(ogg_page *page);
+ int bufferData();
+ Audio::QueuingAudioStream *createAudioStream();
+
+private:
+ Common::SeekableReadStream *_fileStream;
+ Graphics::Surface *_surface;
+ Common::Rational _frameRate;
+ uint32 _frameCount;
+
+ Audio::Mixer *_mixer;
+ Audio::Mixer::SoundType _soundType;
+ Audio::SoundHandle *_audHandle;
+ Audio::QueuingAudioStream *_audStream;
+
+ ogg_sync_state _oggSync;
+ ogg_page _oggPage;
+ ogg_packet _oggPacket;
+ ogg_stream_state _vorbisOut;
+ ogg_stream_state _theoraOut;
+ th_info _theoraInfo;
+ th_comment _theoraComment;
+ th_dec_ctx *_theoraDecode;
+ th_setup_info *_theoraSetup;
+ vorbis_info _vorbisInfo;
+ vorbis_dsp_state _vorbisDSP;
+ vorbis_block _vorbisBlock;
+ vorbis_comment _vorbisComment;
+
+ int _theoraPacket;
+ int _vorbisPacket;
+ bool _stateFlag;
+
+ int _ppLevelMax;
+ int _ppLevel;
+ int _ppInc;
+
+ // single frame video buffering
+ bool _videobufReady;
+ ogg_int64_t _videobufGranulePos;
+ double _videobufTime;
+
+ // single audio fragment audio buffering
+ int _audiobufFill;
+ bool _audiobufReady;
+ ogg_int16_t *_audiobuf;
+ ogg_int64_t _audiobufGranulePos; // time position of last sample
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/fmv/yuvtorgba.cpp b/engines/sword25/fmv/yuvtorgba.cpp
new file mode 100644
index 0000000000..12d11b6784
--- /dev/null
+++ b/engines/sword25/fmv/yuvtorgba.cpp
@@ -0,0 +1,243 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/fmv/yuvtorgba.h"
+
+namespace Sword25 {
+
+static const int PRECISION = 32768;
+static const int COEFFS_Y[256] = {
+ -593888, -555746, -517604, -479462, -441320, -403178, -365036, -326894, -288752, -250610, -212468, -174326, -136184, -98042, -59900, -21758,
+ 16384, 54526, 92668, 130810, 168952, 207094, 245236, 283378, 321520, 359662, 397804, 435946, 474088, 512230, 550372, 588514,
+ 626656, 664798, 702940, 741082, 779224, 817366, 855508, 893650, 931792, 969934, 1008076, 1046218, 1084360, 1122502, 1160644, 1198786,
+ 1236928, 1275070, 1313212, 1351354, 1389496, 1427638, 1465780, 1503922, 1542064, 1580206, 1618348, 1656490, 1694632, 1732774, 1770916, 1809058,
+ 1847200, 1885342, 1923484, 1961626, 1999768, 2037910, 2076052, 2114194, 2152336, 2190478, 2228620, 2266762, 2304904, 2343046, 2381188, 2419330,
+ 2457472, 2495614, 2533756, 2571898, 2610040, 2648182, 2686324, 2724466, 2762608, 2800750, 2838892, 2877034, 2915176, 2953318, 2991460, 3029602,
+ 3067744, 3105886, 3144028, 3182170, 3220312, 3258454, 3296596, 3334738, 3372880, 3411022, 3449164, 3487306, 3525448, 3563590, 3601732, 3639874,
+ 3678016, 3716158, 3754300, 3792442, 3830584, 3868726, 3906868, 3945010, 3983152, 4021294, 4059436, 4097578, 4135720, 4173862, 4212004, 4250146,
+ 4288288, 4326430, 4364572, 4402714, 4440856, 4478998, 4517140, 4555282, 4593424, 4631566, 4669708, 4707850, 4745992, 4784134, 4822276, 4860418,
+ 4898560, 4936702, 4974844, 5012986, 5051128, 5089270, 5127412, 5165554, 5203696, 5241838, 5279980, 5318122, 5356264, 5394406, 5432548, 5470690,
+ 5508832, 5546974, 5585116, 5623258, 5661400, 5699542, 5737684, 5775826, 5813968, 5852110, 5890252, 5928394, 5966536, 6004678, 6042820, 6080962,
+ 6119104, 6157246, 6195388, 6233530, 6271672, 6309814, 6347956, 6386098, 6424240, 6462382, 6500524, 6538666, 6576808, 6614950, 6653092, 6691234,
+ 6729376, 6767518, 6805660, 6843802, 6881944, 6920086, 6958228, 6996370, 7034512, 7072654, 7110796, 7148938, 7187080, 7225222, 7263364, 7301506,
+ 7339648, 7377790, 7415932, 7454074, 7492216, 7530358, 7568500, 7606642, 7644784, 7682926, 7721068, 7759210, 7797352, 7835494, 7873636, 7911778,
+ 7949920, 7988062, 8026204, 8064346, 8102488, 8140630, 8178772, 8216914, 8255056, 8293198, 8331340, 8369482, 8407624, 8445766, 8483908, 8522050,
+ 8560192, 8598334, 8636476, 8674618, 8712760, 8750902, 8789044, 8827186, 8865328, 8903470, 8941612, 8979754, 9017896, 9056038, 9094180, 9132322,
+};
+static const int COEFFS_RV[256] = {
+ -6694144, -6641846, -6589548, -6537250, -6484952, -6432654, -6380356, -6328058, -6275760, -6223462, -6171164, -6118866, -6066568, -6014270, -5961972, -5909674,
+ -5857376, -5805078, -5752780, -5700482, -5648184, -5595886, -5543588, -5491290, -5438992, -5386694, -5334396, -5282098, -5229800, -5177502, -5125204, -5072906,
+ -5020608, -4968310, -4916012, -4863714, -4811416, -4759118, -4706820, -4654522, -4602224, -4549926, -4497628, -4445330, -4393032, -4340734, -4288436, -4236138,
+ -4183840, -4131542, -4079244, -4026946, -3974648, -3922350, -3870052, -3817754, -3765456, -3713158, -3660860, -3608562, -3556264, -3503966, -3451668, -3399370,
+ -3347072, -3294774, -3242476, -3190178, -3137880, -3085582, -3033284, -2980986, -2928688, -2876390, -2824092, -2771794, -2719496, -2667198, -2614900, -2562602,
+ -2510304, -2458006, -2405708, -2353410, -2301112, -2248814, -2196516, -2144218, -2091920, -2039622, -1987324, -1935026, -1882728, -1830430, -1778132, -1725834,
+ -1673536, -1621238, -1568940, -1516642, -1464344, -1412046, -1359748, -1307450, -1255152, -1202854, -1150556, -1098258, -1045960, -993662, -941364, -889066,
+ -836768, -784470, -732172, -679874, -627576, -575278, -522980, -470682, -418384, -366086, -313788, -261490, -209192, -156894, -104596, -52298,
+ 0, 52298, 104596, 156894, 209192, 261490, 313788, 366086, 418384, 470682, 522980, 575278, 627576, 679874, 732172, 784470,
+ 836768, 889066, 941364, 993662, 1045960, 1098258, 1150556, 1202854, 1255152, 1307450, 1359748, 1412046, 1464344, 1516642, 1568940, 1621238,
+ 1673536, 1725834, 1778132, 1830430, 1882728, 1935026, 1987324, 2039622, 2091920, 2144218, 2196516, 2248814, 2301112, 2353410, 2405708, 2458006,
+ 2510304, 2562602, 2614900, 2667198, 2719496, 2771794, 2824092, 2876390, 2928688, 2980986, 3033284, 3085582, 3137880, 3190178, 3242476, 3294774,
+ 3347072, 3399370, 3451668, 3503966, 3556264, 3608562, 3660860, 3713158, 3765456, 3817754, 3870052, 3922350, 3974648, 4026946, 4079244, 4131542,
+ 4183840, 4236138, 4288436, 4340734, 4393032, 4445330, 4497628, 4549926, 4602224, 4654522, 4706820, 4759118, 4811416, 4863714, 4916012, 4968310,
+ 5020608, 5072906, 5125204, 5177502, 5229800, 5282098, 5334396, 5386694, 5438992, 5491290, 5543588, 5595886, 5648184, 5700482, 5752780, 5805078,
+ 5857376, 5909674, 5961972, 6014270, 6066568, 6118866, 6171164, 6223462, 6275760, 6328058, 6380356, 6432654, 6484952, 6537250, 6589548, 6641846,
+};
+static const int COEFFS_GU[256] = {
+ 1639936, 1627124, 1614312, 1601500, 1588688, 1575876, 1563064, 1550252, 1537440, 1524628, 1511816, 1499004, 1486192, 1473380, 1460568, 1447756,
+ 1434944, 1422132, 1409320, 1396508, 1383696, 1370884, 1358072, 1345260, 1332448, 1319636, 1306824, 1294012, 1281200, 1268388, 1255576, 1242764,
+ 1229952, 1217140, 1204328, 1191516, 1178704, 1165892, 1153080, 1140268, 1127456, 1114644, 1101832, 1089020, 1076208, 1063396, 1050584, 1037772,
+ 1024960, 1012148, 999336, 986524, 973712, 960900, 948088, 935276, 922464, 909652, 896840, 884028, 871216, 858404, 845592, 832780,
+ 819968, 807156, 794344, 781532, 768720, 755908, 743096, 730284, 717472, 704660, 691848, 679036, 666224, 653412, 640600, 627788,
+ 614976, 602164, 589352, 576540, 563728, 550916, 538104, 525292, 512480, 499668, 486856, 474044, 461232, 448420, 435608, 422796,
+ 409984, 397172, 384360, 371548, 358736, 345924, 333112, 320300, 307488, 294676, 281864, 269052, 256240, 243428, 230616, 217804,
+ 204992, 192180, 179368, 166556, 153744, 140932, 128120, 115308, 102496, 89684, 76872, 64060, 51248, 38436, 25624, 12812,
+ 0, -12812, -25624, -38436, -51248, -64060, -76872, -89684, -102496, -115308, -128120, -140932, -153744, -166556, -179368, -192180,
+ -204992, -217804, -230616, -243428, -256240, -269052, -281864, -294676, -307488, -320300, -333112, -345924, -358736, -371548, -384360, -397172,
+ -409984, -422796, -435608, -448420, -461232, -474044, -486856, -499668, -512480, -525292, -538104, -550916, -563728, -576540, -589352, -602164,
+ -614976, -627788, -640600, -653412, -666224, -679036, -691848, -704660, -717472, -730284, -743096, -755908, -768720, -781532, -794344, -807156,
+ -819968, -832780, -845592, -858404, -871216, -884028, -896840, -909652, -922464, -935276, -948088, -960900, -973712, -986524, -999336, -1012148,
+ -1024960, -1037772, -1050584, -1063396, -1076208, -1089020, -1101832, -1114644, -1127456, -1140268, -1153080, -1165892, -1178704, -1191516, -1204328, -1217140,
+ -1229952, -1242764, -1255576, -1268388, -1281200, -1294012, -1306824, -1319636, -1332448, -1345260, -1358072, -1370884, -1383696, -1396508, -1409320, -1422132,
+ -1434944, -1447756, -1460568, -1473380, -1486192, -1499004, -1511816, -1524628, -1537440, -1550252, -1563064, -1575876, -1588688, -1601500, -1614312, -1627124,
+};
+static const int COEFFS_GV[256] = {
+ 3409920, 3383280, 3356640, 3330000, 3303360, 3276720, 3250080, 3223440, 3196800, 3170160, 3143520, 3116880, 3090240, 3063600, 3036960, 3010320,
+ 2983680, 2957040, 2930400, 2903760, 2877120, 2850480, 2823840, 2797200, 2770560, 2743920, 2717280, 2690640, 2664000, 2637360, 2610720, 2584080,
+ 2557440, 2530800, 2504160, 2477520, 2450880, 2424240, 2397600, 2370960, 2344320, 2317680, 2291040, 2264400, 2237760, 2211120, 2184480, 2157840,
+ 2131200, 2104560, 2077920, 2051280, 2024640, 1998000, 1971360, 1944720, 1918080, 1891440, 1864800, 1838160, 1811520, 1784880, 1758240, 1731600,
+ 1704960, 1678320, 1651680, 1625040, 1598400, 1571760, 1545120, 1518480, 1491840, 1465200, 1438560, 1411920, 1385280, 1358640, 1332000, 1305360,
+ 1278720, 1252080, 1225440, 1198800, 1172160, 1145520, 1118880, 1092240, 1065600, 1038960, 1012320, 985680, 959040, 932400, 905760, 879120,
+ 852480, 825840, 799200, 772560, 745920, 719280, 692640, 666000, 639360, 612720, 586080, 559440, 532800, 506160, 479520, 452880,
+ 426240, 399600, 372960, 346320, 319680, 293040, 266400, 239760, 213120, 186480, 159840, 133200, 106560, 79920, 53280, 26640,
+ 0, -26640, -53280, -79920, -106560, -133200, -159840, -186480, -213120, -239760, -266400, -293040, -319680, -346320, -372960, -399600,
+ -426240, -452880, -479520, -506160, -532800, -559440, -586080, -612720, -639360, -666000, -692640, -719280, -745920, -772560, -799200, -825840,
+ -852480, -879120, -905760, -932400, -959040, -985680, -1012320, -1038960, -1065600, -1092240, -1118880, -1145520, -1172160, -1198800, -1225440, -1252080,
+ -1278720, -1305360, -1332000, -1358640, -1385280, -1411920, -1438560, -1465200, -1491840, -1518480, -1545120, -1571760, -1598400, -1625040, -1651680, -1678320,
+ -1704960, -1731600, -1758240, -1784880, -1811520, -1838160, -1864800, -1891440, -1918080, -1944720, -1971360, -1998000, -2024640, -2051280, -2077920, -2104560,
+ -2131200, -2157840, -2184480, -2211120, -2237760, -2264400, -2291040, -2317680, -2344320, -2370960, -2397600, -2424240, -2450880, -2477520, -2504160, -2530800,
+ -2557440, -2584080, -2610720, -2637360, -2664000, -2690640, -2717280, -2743920, -2770560, -2797200, -2823840, -2850480, -2877120, -2903760, -2930400, -2957040,
+ -2983680, -3010320, -3036960, -3063600, -3090240, -3116880, -3143520, -3170160, -3196800, -3223440, -3250080, -3276720, -3303360, -3330000, -3356640, -3383280,
+};
+static const int COEFFS_BU[256] = {
+ -8464128, -8398002, -8331876, -8265750, -8199624, -8133498, -8067372, -8001246, -7935120, -7868994, -7802868, -7736742, -7670616, -7604490, -7538364, -7472238,
+ -7406112, -7339986, -7273860, -7207734, -7141608, -7075482, -7009356, -6943230, -6877104, -6810978, -6744852, -6678726, -6612600, -6546474, -6480348, -6414222,
+ -6348096, -6281970, -6215844, -6149718, -6083592, -6017466, -5951340, -5885214, -5819088, -5752962, -5686836, -5620710, -5554584, -5488458, -5422332, -5356206,
+ -5290080, -5223954, -5157828, -5091702, -5025576, -4959450, -4893324, -4827198, -4761072, -4694946, -4628820, -4562694, -4496568, -4430442, -4364316, -4298190,
+ -4232064, -4165938, -4099812, -4033686, -3967560, -3901434, -3835308, -3769182, -3703056, -3636930, -3570804, -3504678, -3438552, -3372426, -3306300, -3240174,
+ -3174048, -3107922, -3041796, -2975670, -2909544, -2843418, -2777292, -2711166, -2645040, -2578914, -2512788, -2446662, -2380536, -2314410, -2248284, -2182158,
+ -2116032, -2049906, -1983780, -1917654, -1851528, -1785402, -1719276, -1653150, -1587024, -1520898, -1454772, -1388646, -1322520, -1256394, -1190268, -1124142,
+ -1058016, -991890, -925764, -859638, -793512, -727386, -661260, -595134, -529008, -462882, -396756, -330630, -264504, -198378, -132252, -66126,
+ 0, 66126, 132252, 198378, 264504, 330630, 396756, 462882, 529008, 595134, 661260, 727386, 793512, 859638, 925764, 991890,
+ 1058016, 1124142, 1190268, 1256394, 1322520, 1388646, 1454772, 1520898, 1587024, 1653150, 1719276, 1785402, 1851528, 1917654, 1983780, 2049906,
+ 2116032, 2182158, 2248284, 2314410, 2380536, 2446662, 2512788, 2578914, 2645040, 2711166, 2777292, 2843418, 2909544, 2975670, 3041796, 3107922,
+ 3174048, 3240174, 3306300, 3372426, 3438552, 3504678, 3570804, 3636930, 3703056, 3769182, 3835308, 3901434, 3967560, 4033686, 4099812, 4165938,
+ 4232064, 4298190, 4364316, 4430442, 4496568, 4562694, 4628820, 4694946, 4761072, 4827198, 4893324, 4959450, 5025576, 5091702, 5157828, 5223954,
+ 5290080, 5356206, 5422332, 5488458, 5554584, 5620710, 5686836, 5752962, 5819088, 5885214, 5951340, 6017466, 6083592, 6149718, 6215844, 6281970,
+ 6348096, 6414222, 6480348, 6546474, 6612600, 6678726, 6744852, 6810978, 6877104, 6943230, 7009356, 7075482, 7141608, 7207734, 7273860, 7339986,
+ 7406112, 7472238, 7538364, 7604490, 7670616, 7736742, 7802868, 7868994, 7935120, 8001246, 8067372, 8133498, 8199624, 8265750, 8331876, 8398002,
+};
+static const int CLAMP_TAB[1024] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+};
+
+void YUVtoBGRA::translate(th_ycbcr_buffer &YUVBuffer, const th_info &theoraInfo, byte *pixelData, int pixelsSize) {
+
+ // Width and height of all buffers have to be divisible by 2.
+ BS_ASSERT((YUVBuffer[0].width & 1) == 0);
+ BS_ASSERT((YUVBuffer[0].height & 1) == 0);
+ BS_ASSERT((YUVBuffer[1].width & 1) == 0);
+ BS_ASSERT((YUVBuffer[2].width & 1) == 0);
+
+ // UV images have to have a quarter of the Y image resolution
+ BS_ASSERT(YUVBuffer[1].width == YUVBuffer[0].width >> 1);
+ BS_ASSERT(YUVBuffer[2].width == YUVBuffer[0].width >> 1);
+ BS_ASSERT(YUVBuffer[1].height == YUVBuffer[0].height >> 1);
+ BS_ASSERT(YUVBuffer[2].height == YUVBuffer[0].height >> 1);
+
+ const int *cl = &CLAMP_TAB[320];
+
+ const byte *ySrc0 = YUVBuffer[0].data;
+ const byte *ySrc1 = YUVBuffer[0].data + YUVBuffer[0].stride;
+ const byte *uSrc = YUVBuffer[1].data;
+ const byte *vSrc = YUVBuffer[2].data;
+ byte *dst0 = &pixelData[0];
+ byte *dst1 = &pixelData[0] + YUVBuffer[0].width * 4;
+
+ for (int h = 0; h < YUVBuffer[0].height / 2; ++h) {
+ for (int w = 0; w < YUVBuffer[0].width / 2; ++w) {
+ int u = *uSrc++;
+ int v = *vSrc++;
+
+ int rUV = COEFFS_RV[v];
+ int gUV = COEFFS_GU[u] + COEFFS_GV[v];
+ int bUV = COEFFS_BU[u];
+
+ int y = *ySrc0++;
+ int r = COEFFS_Y[y] + rUV;
+ int g = COEFFS_Y[y] + gUV;
+ int b = COEFFS_Y[y] + bUV;
+ *dst0++ = cl[b / PRECISION];
+ *dst0++ = cl[g / PRECISION];
+ *dst0++ = cl[r / PRECISION];
+ *dst0++ = 255;
+
+ y = *ySrc1++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst1++ = cl[b / PRECISION];
+ *dst1++ = cl[g / PRECISION];
+ *dst1++ = cl[r / PRECISION];
+ *dst1++ = 255;
+
+ y = *ySrc0++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst0++ = cl[b / PRECISION];
+ *dst0++ = cl[g / PRECISION];
+ *dst0++ = cl[r / PRECISION];
+ *dst0++ = 255;
+
+ y = *ySrc1++;
+ r = COEFFS_Y[y] + rUV;
+ g = COEFFS_Y[y] + gUV;
+ b = COEFFS_Y[y] + bUV;
+ *dst1++ = cl[b / PRECISION];
+ *dst1++ = cl[g / PRECISION];
+ *dst1++ = cl[r / PRECISION];
+ *dst1++ = 255;
+ }
+
+ dst0 += YUVBuffer[0].width * 4;
+ dst1 += YUVBuffer[0].width * 4;
+ ySrc0 += YUVBuffer[0].stride * 2 - YUVBuffer[0].width;
+ ySrc1 += YUVBuffer[0].stride * 2 - YUVBuffer[0].width;
+ uSrc += YUVBuffer[1].stride - YUVBuffer[1].width;
+ vSrc += YUVBuffer[2].stride - YUVBuffer[2].width;
+ }
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/fmv/yuvtorgba.h b/engines/sword25/fmv/yuvtorgba.h
new file mode 100644
index 0000000000..15fa85b7e4
--- /dev/null
+++ b/engines/sword25/fmv/yuvtorgba.h
@@ -0,0 +1,51 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_YUVTORGBA_H
+#define SWORD25_YUVTORGBA_H
+
+#include "sword25/kernel/common.h"
+#include <theora/theora.h>
+#include <theora/codec.h>
+
+namespace Sword25 {
+
+class YUVtoBGRA {
+public:
+ static void translate(th_ycbcr_buffer &YUVBuffer, const th_info &theoraInfo, byte *pixelData, int pixelsSize);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animation.cpp b/engines/sword25/gfx/animation.cpp
new file mode 100644
index 0000000000..7ec445acb8
--- /dev/null
+++ b/engines/sword25/gfx/animation.cpp
@@ -0,0 +1,711 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/animation.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/resmanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/callbackregistry.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "ANIMATION"
+
+Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Vom negativen Fall ausgehen.
+ _initSuccess = false;
+
+ initializeAnimationResource(fileName);
+
+ // Erfolg signalisieren.
+ _initSuccess = true;
+}
+
+Animation::Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &templ) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Vom negativen Fall ausgehen.
+ _initSuccess = false;
+
+ _animationTemplateHandle = AnimationTemplate::create(templ);
+
+ // Erfolg signalisieren.
+ _initSuccess = true;
+}
+
+Animation::Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ TimedRenderObject(parentPtr, RenderObject::TYPE_ANIMATION, handle) {
+ // Das BS_RenderObject konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ initMembers();
+
+ // Objekt vom Stream laden.
+ _initSuccess = unpersist(reader);
+}
+
+void Animation::initializeAnimationResource(const Common::String &fileName) {
+ // Die Resource wird für die gesamte Lebensdauer des Animations-Objektes gelockt.
+ Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(fileName);
+ if (resourcePtr && resourcePtr->GetType() == Resource::TYPE_ANIMATION)
+ _animationResourcePtr = static_cast<AnimationResource *>(resourcePtr);
+ else {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested. The Animation can't be created.", fileName.c_str());
+ return;
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen.
+ computeCurrentCharacteristics();
+}
+
+void Animation::initMembers() {
+ _currentFrame = 0;
+ _currentFrameTime = 0;
+ _direction = FORWARD;
+ _running = false;
+ _finished = false;
+ _relX = 0;
+ _relY = 0;
+ _scaleFactorX = 1.0f;
+ _scaleFactorY = 1.0f;
+ _modulationColor = 0xffffffff;
+ _animationResourcePtr = 0;
+ _animationTemplateHandle = 0;
+ _framesLocked = false;
+}
+
+Animation::~Animation() {
+ if (getAnimationDescription()) {
+ stop();
+ getAnimationDescription()->unlock();
+ }
+
+ // Delete Callbacks
+ Common::Array<ANIMATION_CALLBACK_DATA>::iterator it = _deleteCallbacks.begin();
+ for (; it != _deleteCallbacks.end(); it++)((*it).Callback)((*it).Data);
+
+}
+
+void Animation::play() {
+ // Wenn die Animation zuvor komplett durchgelaufen ist, wird sie wieder von Anfang abgespielt
+ if (_finished)
+ stop();
+
+ _running = true;
+ lockAllFrames();
+}
+
+void Animation::pause() {
+ _running = false;
+ unlockAllFrames();
+}
+
+void Animation::stop() {
+ _currentFrame = 0;
+ _currentFrameTime = 0;
+ _direction = FORWARD;
+ pause();
+}
+
+void Animation::setFrame(uint nr) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+
+ if (nr >= animationDescriptionPtr->getFrameCount()) {
+ BS_LOG_ERRORLN("Tried to set animation to illegal frame (%d). Value must be between 0 and %d.",
+ nr, animationDescriptionPtr->getFrameCount());
+ return;
+ }
+
+ _currentFrame = nr;
+ _currentFrameTime = 0;
+ computeCurrentCharacteristics();
+ forceRefresh();
+}
+
+bool Animation::doRender() {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(_currentFrame < animationDescriptionPtr->getFrameCount());
+
+ // Bitmap des aktuellen Frames holen
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(_currentFrame).fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
+
+ // Framebufferobjekt holen
+ GraphicEngine *pGfx = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool result;
+ if (isScalingAllowed() && (_width != pBitmapResource->getWidth() || _height != pBitmapResource->getHeight())) {
+ result = pBitmapResource->blit(_absoluteX, _absoluteY,
+ (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ } else {
+ result = pBitmapResource->blit(_absoluteX, _absoluteY,
+ (animationDescriptionPtr->getFrame(_currentFrame).flipV ? BitmapResource::FLIP_V : 0) |
+ (animationDescriptionPtr->getFrame(_currentFrame).flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ }
+
+ // Resource freigeben
+ pBitmapResource->release();
+
+ return result;
+}
+
+void Animation::frameNotification(int timeElapsed) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ BS_ASSERT(timeElapsed >= 0);
+
+ // Nur wenn die Animation läuft wird sie auch weiterbewegt
+ if (_running) {
+ // Gesamte vergangene Zeit bestimmen (inkl. Restzeit des aktuellen Frames)
+ _currentFrameTime += timeElapsed;
+
+ // Anzahl an zu überpringenden Frames bestimmen
+ int skipFrames = animationDescriptionPtr->getMillisPerFrame() == 0 ? 0 : _currentFrameTime / animationDescriptionPtr->getMillisPerFrame();
+
+ // Neue Frame-Restzeit bestimmen
+ _currentFrameTime -= animationDescriptionPtr->getMillisPerFrame() * skipFrames;
+
+ // Neuen Frame bestimmen (je nach aktuellener Abspielrichtung wird addiert oder subtrahiert)
+ int tmpCurFrame = _currentFrame;
+ switch (_direction) {
+ case FORWARD:
+ tmpCurFrame += skipFrames;
+ break;
+
+ case BACKWARD:
+ tmpCurFrame -= skipFrames;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+
+ // Überläufe behandeln
+ if (tmpCurFrame < 0) {
+ // Loop-Point Callbacks
+ for (uint i = 0; i < _loopPointCallbacks.size();) {
+ if ((_loopPointCallbacks[i].Callback)(_loopPointCallbacks[i].Data) == false) {
+ _loopPointCallbacks.remove_at(i);
+ } else
+ i++;
+ }
+
+ // Ein Unterlauf darf nur auftreten, wenn der Animationstyp JOJO ist.
+ BS_ASSERT(animationDescriptionPtr->getAnimationType() == AT_JOJO);
+ tmpCurFrame = - tmpCurFrame;
+ _direction = FORWARD;
+ } else if (static_cast<uint>(tmpCurFrame) >= animationDescriptionPtr->getFrameCount()) {
+ // Loop-Point Callbacks
+ for (uint i = 0; i < _loopPointCallbacks.size();) {
+ if ((_loopPointCallbacks[i].Callback)(_loopPointCallbacks[i].Data) == false) {
+ _loopPointCallbacks.remove_at(i);
+ } else
+ i++;
+ }
+
+ switch (animationDescriptionPtr->getAnimationType()) {
+ case AT_ONESHOT:
+ tmpCurFrame = animationDescriptionPtr->getFrameCount() - 1;
+ _finished = true;
+ pause();
+ break;
+
+ case AT_LOOP:
+ tmpCurFrame = tmpCurFrame % animationDescriptionPtr->getFrameCount();
+ break;
+
+ case AT_JOJO:
+ tmpCurFrame = animationDescriptionPtr->getFrameCount() - (tmpCurFrame % animationDescriptionPtr->getFrameCount()) - 1;
+ _direction = BACKWARD;
+ break;
+
+ default:
+ BS_ASSERT(0);
+ }
+ }
+
+ if ((int)_currentFrame != tmpCurFrame) {
+ forceRefresh();
+
+ if (animationDescriptionPtr->getFrame(_currentFrame).action != "") {
+ // Action Callbacks
+ for (uint i = 0; i < _actionCallbacks.size();) {
+ if ((_actionCallbacks[i].Callback)(_actionCallbacks[i].Data) == false) {
+ _actionCallbacks.remove_at(i);
+ } else
+ i++;
+ }
+ }
+ }
+
+ _currentFrame = static_cast<uint>(tmpCurFrame);
+ }
+
+ // Größe und Position der Animation anhand des aktuellen Frames bestimmen
+ computeCurrentCharacteristics();
+
+ BS_ASSERT(_currentFrame < animationDescriptionPtr->getFrameCount());
+ BS_ASSERT(_currentFrameTime >= 0);
+}
+
+void Animation::computeCurrentCharacteristics() {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ // Größe des Bitmaps auf die Animation übertragen
+ _width = static_cast<int>(pBitmap->getWidth() * _scaleFactorX);
+ _height = static_cast<int>(pBitmap->getHeight() * _scaleFactorY);
+
+ // Position anhand des Hotspots berechnen und setzen
+ int posX = _relX + computeXModifier();
+ int posY = _relY + computeYModifier();
+
+ RenderObject::setPos(posX, posY);
+
+ pBitmap->release();
+}
+
+bool Animation::lockAllFrames() {
+ if (!_framesLocked) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
+ if (!Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(i).fileName)) {
+ BS_LOG_ERRORLN("Could not lock all animation frames.");
+ return false;
+ }
+ }
+
+ _framesLocked = true;
+ }
+
+ return true;
+}
+
+bool Animation::unlockAllFrames() {
+ if (_framesLocked) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ for (uint i = 0; i < animationDescriptionPtr->getFrameCount(); ++i) {
+ Resource *pResource;
+ if (!(pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(animationDescriptionPtr->getFrame(i).fileName))) {
+ BS_LOG_ERRORLN("Could not unlock all animation frames.");
+ return false;
+ }
+
+ // Zwei mal freigeben um den Request von LockAllFrames() und den jetzigen Request aufzuheben
+ pResource->release();
+ if (pResource->GetLockCount())
+ pResource->release();
+ }
+
+ _framesLocked = false;
+ }
+
+ return true;
+}
+
+Animation::ANIMATION_TYPES Animation::getAnimationType() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getAnimationType();
+}
+
+int Animation::getFPS() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFPS();
+}
+
+int Animation::getFrameCount() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFrameCount();
+}
+
+bool Animation::isScalingAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isScalingAllowed();
+}
+
+bool Animation::isAlphaAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isAlphaAllowed();
+}
+
+bool Animation::isColorModulationAllowed() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->isColorModulationAllowed();
+}
+
+void Animation::setPos(int relX, int relY) {
+ _relX = relX;
+ _relY = relY;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setX(int relX) {
+ _relX = relX;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setY(int relY) {
+ _relY = relY;
+
+ computeCurrentCharacteristics();
+}
+
+void Animation::setAlpha(int alpha) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isAlphaAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set alpha value on an animation that does not support alpha. Call was ignored.");
+ return;
+ }
+
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Animation::setModulationColor(uint modulationColor) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isColorModulationAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set modulation color on an animation that does not support color modulation. Call was ignored");
+ return;
+ }
+
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+void Animation::setScaleFactor(float scaleFactor) {
+ setScaleFactorX(scaleFactor);
+ setScaleFactorY(scaleFactor);
+}
+
+void Animation::setScaleFactorX(float scaleFactorX) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set x scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (scaleFactorX != _scaleFactorX) {
+ _scaleFactorX = scaleFactorX;
+ if (_scaleFactorX <= 0.0f)
+ _scaleFactorX = 0.001f;
+ forceRefresh();
+ computeCurrentCharacteristics();
+ }
+}
+
+void Animation::setScaleFactorY(float scaleFactorY) {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ if (!animationDescriptionPtr->isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set y scale factor on an animation that does not support scaling. Call was ignored");
+ return;
+ }
+
+ if (scaleFactorY != _scaleFactorY) {
+ _scaleFactorY = scaleFactorY;
+ if (_scaleFactorY <= 0.0f)
+ _scaleFactorY = 0.001f;
+ forceRefresh();
+ computeCurrentCharacteristics();
+ }
+}
+
+const Common::String &Animation::getCurrentAction() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ return animationDescriptionPtr->getFrame(_currentFrame).action;
+}
+
+int Animation::getX() const {
+ return _relX;
+}
+
+int Animation::getY() const {
+ return _relY;
+}
+
+int Animation::getAbsoluteX() const {
+ return _absoluteX + (_relX - _x);
+}
+
+int Animation::getAbsoluteY() const {
+ return _absoluteY + (_relY - _y);
+}
+
+int Animation::computeXModifier() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ int result = curFrame.flipV ? - static_cast<int>((pBitmap->getWidth() - 1 - curFrame.hotspotX) * _scaleFactorX) :
+ - static_cast<int>(curFrame.hotspotX * _scaleFactorX);
+
+ pBitmap->release();
+
+ return result;
+}
+
+int Animation::computeYModifier() const {
+ AnimationDescription *animationDescriptionPtr = getAnimationDescription();
+ BS_ASSERT(animationDescriptionPtr);
+ const AnimationResource::Frame &curFrame = animationDescriptionPtr->getFrame(_currentFrame);
+
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(curFrame.fileName);
+ BS_ASSERT(pResource);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmap = static_cast<BitmapResource *>(pResource);
+
+ int result = curFrame.flipH ? - static_cast<int>((pBitmap->getHeight() - 1 - curFrame.hotspotY) * _scaleFactorY) :
+ - static_cast<int>(curFrame.hotspotY * _scaleFactorY);
+
+ pBitmap->release();
+
+ return result;
+}
+
+void Animation::registerActionCallback(ANIMATION_CALLBACK callback, uint data) {
+ ANIMATION_CALLBACK_DATA cd;
+ cd.Callback = callback;
+ cd.Data = data;
+ _actionCallbacks.push_back(cd);
+}
+
+void Animation::registerLoopPointCallback(ANIMATION_CALLBACK callback, uint data) {
+ ANIMATION_CALLBACK_DATA cd;
+ cd.Callback = callback;
+ cd.Data = data;
+ _loopPointCallbacks.push_back(cd);
+}
+
+void Animation::registerDeleteCallback(ANIMATION_CALLBACK callback, uint data) {
+ ANIMATION_CALLBACK_DATA cd;
+ cd.Callback = callback;
+ cd.Data = data;
+ _deleteCallbacks.push_back(cd);
+}
+
+void Animation::persistCallbackVector(OutputPersistenceBlock &writer, const Common::Array<ANIMATION_CALLBACK_DATA> &vector) {
+ // Anzahl an Callbacks persistieren.
+ writer.write(vector.size());
+
+ // Alle Callbacks einzeln persistieren.
+ Common::Array<ANIMATION_CALLBACK_DATA>::const_iterator it = vector.begin();
+ while (it != vector.end()) {
+ writer.write(CallbackRegistry::getInstance().resolveCallbackPointer((void (*)(int))it->Callback));
+ writer.write(it->Data);
+
+ ++it;
+ }
+}
+
+void Animation::unpersistCallbackVector(InputPersistenceBlock &reader, Common::Array<ANIMATION_CALLBACK_DATA> &vector) {
+ // Callbackvector leeren.
+ vector.resize(0);
+
+ // Anzahl an Callbacks einlesen.
+ uint callbackCount;
+ reader.read(callbackCount);
+
+ // Alle Callbacks einzeln wieder herstellen.
+ for (uint i = 0; i < callbackCount; ++i) {
+ ANIMATION_CALLBACK_DATA callbackData;
+
+ Common::String callbackFunctionName;
+ reader.read(callbackFunctionName);
+ callbackData.Callback = reinterpret_cast<ANIMATION_CALLBACK>(CallbackRegistry::getInstance().resolveCallbackFunction(callbackFunctionName));
+
+ reader.read(callbackData.Data);
+
+ vector.push_back(callbackData);
+ }
+}
+
+bool Animation::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+
+ writer.write(_relX);
+ writer.write(_relY);
+ writer.write(_scaleFactorX);
+ writer.write(_scaleFactorY);
+ writer.write(_modulationColor);
+ writer.write(_currentFrame);
+ writer.write(_currentFrameTime);
+ writer.write(_running);
+ writer.write(_finished);
+ writer.write(static_cast<uint>(_direction));
+
+ // Je nach Animationstyp entweder das Template oder die Ressource speichern.
+ if (_animationResourcePtr) {
+ uint marker = 0;
+ writer.write(marker);
+ writer.write(_animationResourcePtr->getFileName());
+ } else if (_animationTemplateHandle) {
+ uint marker = 1;
+ writer.write(marker);
+ writer.write(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ //writer.write(_AnimationDescriptionPtr);
+
+ writer.write(_framesLocked);
+ persistCallbackVector(writer, _loopPointCallbacks);
+ persistCallbackVector(writer, _actionCallbacks);
+ persistCallbackVector(writer, _deleteCallbacks);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool Animation::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ reader.read(_relX);
+ reader.read(_relY);
+ reader.read(_scaleFactorX);
+ reader.read(_scaleFactorY);
+ reader.read(_modulationColor);
+ reader.read(_currentFrame);
+ reader.read(_currentFrameTime);
+ reader.read(_running);
+ reader.read(_finished);
+ uint direction;
+ reader.read(direction);
+ _direction = static_cast<Direction>(direction);
+
+ // Animationstyp einlesen.
+ uint marker;
+ reader.read(marker);
+ if (marker == 0) {
+ Common::String resourceFilename;
+ reader.read(resourceFilename);
+ initializeAnimationResource(resourceFilename);
+ } else if (marker == 1) {
+ reader.read(_animationTemplateHandle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ reader.read(_framesLocked);
+ if (_framesLocked)
+ lockAllFrames();
+
+ unpersistCallbackVector(reader, _loopPointCallbacks);
+ unpersistCallbackVector(reader, _actionCallbacks);
+ unpersistCallbackVector(reader, _deleteCallbacks);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+// -----------------------------------------------------------------------------
+
+AnimationDescription *Animation::getAnimationDescription() const {
+ if (_animationResourcePtr)
+ return _animationResourcePtr;
+ else
+ return AnimationTemplateRegistry::getInstance().resolveHandle(_animationTemplateHandle);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animation.h b/engines/sword25/gfx/animation.h
new file mode 100644
index 0000000000..0676c0c116
--- /dev/null
+++ b/engines/sword25/gfx/animation.h
@@ -0,0 +1,227 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATION_H
+#define SWORD25_ANIMATION_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/timedrenderobject.h"
+
+namespace Sword25 {
+
+// Forward declarations
+class Kernel;
+class AnimationResource;
+class AnimationTemplate;
+class AnimationDescription;
+class InputPersistenceBlock;
+
+class Animation : public TimedRenderObject {
+ friend class RenderObject;
+
+private:
+ Animation(RenderObjectPtr<RenderObject> parentPtr, const Common::String &fileName);
+ Animation(RenderObjectPtr<RenderObject> parentPtr, const AnimationTemplate &template_);
+ Animation(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ enum ANIMATION_TYPES {
+ AT_ONESHOT,
+ AT_LOOP,
+ AT_JOJO
+ };
+
+ virtual ~Animation();
+
+ void play();
+ void pause();
+ void stop();
+ void setFrame(uint nr);
+
+ virtual void setPos(int x, int y);
+ virtual void setX(int x);
+ virtual void setY(int y);
+
+ virtual int getX() const;
+ virtual int getY() const;
+ virtual int getAbsoluteX() const;
+ virtual int getAbsoluteY() const;
+
+ /**
+ @brief Setzt den Alphawert der Animation.
+ @param Alpha der neue Alphawert der Animation (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Animation.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe der Animation festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void setModulationColor(uint modulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation.
+ @param ScaleFactor der Faktor um den die Animation in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactor(float scaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der X-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorX(float scaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Animation auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Animation in Richtungen der Y-Achse gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorY(float scaleFactorY);
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorX() const {
+ return _scaleFactorX;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter der Animation auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorY() const {
+ return _scaleFactorY;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+ virtual void frameNotification(int timeElapsed);
+
+ ANIMATION_TYPES getAnimationType() const;
+ int getFPS() const;
+ int getFrameCount() const;
+ bool isScalingAllowed() const;
+ bool isAlphaAllowed() const;
+ bool isColorModulationAllowed() const;
+ uint getCurrentFrame() const {
+ return _currentFrame;
+ }
+ const Common::String &getCurrentAction() const;
+ bool isRunning() const {
+ return _running;
+ }
+
+ typedef bool (*ANIMATION_CALLBACK)(uint);
+
+ void registerLoopPointCallback(ANIMATION_CALLBACK callback, uint data = 0);
+ void registerActionCallback(ANIMATION_CALLBACK callback, uint data = 0);
+ void registerDeleteCallback(ANIMATION_CALLBACK Callback, uint Data = 0);
+
+protected:
+ virtual bool doRender();
+
+private:
+ enum Direction {
+ FORWARD,
+ BACKWARD
+ };
+
+ int _relX;
+ int _relY;
+ float _scaleFactorX;
+ float _scaleFactorY;
+ uint _modulationColor;
+ uint _currentFrame;
+ int _currentFrameTime;
+ bool _running;
+ bool _finished;
+ Direction _direction;
+ AnimationResource *_animationResourcePtr;
+ uint _animationTemplateHandle;
+ bool _framesLocked;
+
+ struct ANIMATION_CALLBACK_DATA {
+ ANIMATION_CALLBACK Callback;
+ uint Data;
+ };
+ Common::Array<ANIMATION_CALLBACK_DATA> _loopPointCallbacks;
+ Common::Array<ANIMATION_CALLBACK_DATA> _actionCallbacks;
+ Common::Array<ANIMATION_CALLBACK_DATA> _deleteCallbacks;
+
+ /**
+ @brief Lockt alle Frames.
+ @return Gibt false zurück, falls nicht alle Frames gelockt werden konnten.
+ */
+ bool lockAllFrames();
+
+ /**
+ @brief Unlockt alle Frames.
+ @return Gibt false zurück, falls nicht alles Frames freigegeben werden konnten.
+ */
+ bool unlockAllFrames();
+
+ /**
+ @brief Diese Methode aktualisiert die Parameter (Größe, Position) der Animation anhand des aktuellen Frames.
+
+ Diese Methode muss bei jedem Framewechsel aufgerufen werden damit der RenderObject-Manager immer aktuelle Daten hat.
+ */
+ void computeCurrentCharacteristics();
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int computeXModifier() const;
+
+ /**
+ @brief Berechnet den Abstand zwischen dem linken Rand und dem Hotspot auf X-Achse in der aktuellen Darstellung.
+ */
+ int computeYModifier() const;
+
+ void initMembers();
+ void persistCallbackVector(OutputPersistenceBlock &writer, const Common::Array<ANIMATION_CALLBACK_DATA> &vector);
+ void unpersistCallbackVector(InputPersistenceBlock &reader, Common::Array<ANIMATION_CALLBACK_DATA> &vector);
+ AnimationDescription *getAnimationDescription() const;
+ void initializeAnimationResource(const Common::String &fileName);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationdescription.cpp b/engines/sword25/gfx/animationdescription.cpp
new file mode 100644
index 0000000000..68ba7b63a6
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.cpp
@@ -0,0 +1,65 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/animationdescription.h"
+
+namespace Sword25 {
+
+bool AnimationDescription::persist(OutputPersistenceBlock &writer) {
+ writer.write(static_cast<uint>(_animationType));
+ writer.write(_FPS);
+ writer.write(_millisPerFrame);
+ writer.write(_scalingAllowed);
+ writer.write(_alphaAllowed);
+ writer.write(_colorModulationAllowed);
+
+ return true;
+}
+
+bool AnimationDescription::unpersist(InputPersistenceBlock &reader) {
+ uint animationType;
+ reader.read(animationType);
+ _animationType = static_cast<Animation::ANIMATION_TYPES>(animationType);
+ reader.read(_FPS);
+ reader.read(_millisPerFrame);
+ reader.read(_scalingAllowed);
+ reader.read(_alphaAllowed);
+ reader.read(_colorModulationAllowed);
+
+ return reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationdescription.h b/engines/sword25/gfx/animationdescription.h
new file mode 100644
index 0000000000..a52f5d3f68
--- /dev/null
+++ b/engines/sword25/gfx/animationdescription.h
@@ -0,0 +1,103 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONDESCRIPTION_H
+#define SWORD25_ANIMATIONDESCRIPTION_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/animation.h"
+
+namespace Sword25 {
+
+class AnimationDescription : public Persistable {
+protected:
+ AnimationDescription() :
+ _animationType(Animation::AT_LOOP),
+ _FPS(10),
+ _millisPerFrame(0),
+ _scalingAllowed(true),
+ _alphaAllowed(true),
+ _colorModulationAllowed(true)
+ {};
+
+public:
+ struct Frame {
+ // Die Hotspot-Angabe bezieht sich auf das ungeflippte Bild!!
+ int hotspotX;
+ int hotspotY;
+ bool flipV;
+ bool flipH;
+ Common::String fileName;
+ Common::String action;
+ };
+
+ virtual const Frame &getFrame(uint index) const = 0;
+ virtual uint getFrameCount() const = 0;
+ virtual void unlock() = 0;
+
+ Animation::ANIMATION_TYPES getAnimationType() const {
+ return _animationType;
+ }
+ int getFPS() const {
+ return _FPS;
+ }
+ int getMillisPerFrame() const {
+ return _millisPerFrame;
+ }
+ bool isScalingAllowed() const {
+ return _scalingAllowed;
+ }
+ bool isAlphaAllowed() const {
+ return _alphaAllowed;
+ }
+ bool isColorModulationAllowed() const {
+ return _colorModulationAllowed;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ Animation::ANIMATION_TYPES _animationType;
+ int _FPS;
+ int _millisPerFrame;
+ bool _scalingAllowed;
+ bool _alphaAllowed;
+ bool _colorModulationAllowed;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp
new file mode 100644
index 0000000000..93b5934041
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -0,0 +1,254 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/animationresource.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/string.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/bitmapresource.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "ANIMATIONRESOURCE"
+
+namespace {
+const int DEFAULT_FPS = 10;
+const int MIN_FPS = 1;
+const int MAX_FPS = 200;
+}
+
+AnimationResource::AnimationResource(const Common::String &filename) :
+ Resource(filename, Resource::TYPE_ANIMATION),
+ Common::XMLParser(),
+ _valid(false) {
+ // Get a pointer to the package manager
+ Kernel *pKernel = Kernel::GetInstance();
+ _pPackage = static_cast<PackageManager *>(pKernel->GetService("package"));
+ BS_ASSERT(_pPackage);
+
+ // Switch to the folder the specified Xml fiile is in
+ Common::String oldDirectory = _pPackage->getCurrentDirectory();
+ if (getFileName().contains('/')) {
+ Common::String dir = Common::String(getFileName().c_str(), strrchr(getFileName().c_str(), '/'));
+ _pPackage->changeDirectory(dir);
+ }
+
+ // Load the contents of the file
+ uint fileSize;
+ char *xmlData = _pPackage->getXmlFile(getFileName(), &fileSize);
+ if (!xmlData) {
+ BS_LOG_ERRORLN("Could not read \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Parse the contents
+ if (!loadBuffer((const byte *)xmlData, fileSize))
+ return;
+
+ _valid = parse();
+ close();
+ free(xmlData);
+
+ // Switch back to the previous folder
+ _pPackage->changeDirectory(oldDirectory);
+
+ // Give an error message if there weren't any frames specified
+ if (_frames.empty()) {
+ BS_LOG_ERRORLN("\"%s\" does not have any frames.", getFileName().c_str());
+ return;
+ }
+
+ // Pre-cache all the frames
+ if (!precacheAllFrames()) {
+ BS_LOG_ERRORLN("Could not precache all frames of \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Post processing to compute animation features
+ if (!computeFeatures()) {
+ BS_LOG_ERRORLN("Could not determine the features of \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ _valid = true;
+}
+
+bool AnimationResource::parseBooleanKey(Common::String s, bool &result) {
+ s.toLowercase();
+ if (!strcmp(s.c_str(), "true"))
+ result = true;
+ else if (!strcmp(s.c_str(), "false"))
+ result = false;
+ else
+ return false;
+ return true;
+}
+
+bool AnimationResource::parserCallback_animation(ParserNode *node) {
+ if (!parseIntegerKey(node->values["fps"].c_str(), 1, &_FPS) || (_FPS < MIN_FPS) || (_FPS > MAX_FPS)) {
+ return parserError("Illegal or missing fps attribute in <animation> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_FPS);
+ }
+
+ // Loop type value
+ const char *loopTypeString = node->values["type"].c_str();
+
+ if (strcmp(loopTypeString, "oneshot") == 0) {
+ _animationType = Animation::AT_ONESHOT;
+ } else if (strcmp(loopTypeString, "loop") == 0) {
+ _animationType = Animation::AT_LOOP;
+ } else if (strcmp(loopTypeString, "jojo") == 0) {
+ _animationType = Animation::AT_JOJO;
+ } else {
+ BS_LOG_WARNINGLN("Illegal type value (\"%s\") in <animation> tag in \"%s\". Assuming default (\"loop\").",
+ loopTypeString, getFileName().c_str());
+ _animationType = Animation::AT_LOOP;
+ }
+
+ // Calculate the milliseconds required per frame
+ // FIXME: Double check variable naming. Based on the constant, it may be microseconds
+ _millisPerFrame = 1000000 / _FPS;
+
+ return true;
+}
+
+bool AnimationResource::parserCallback_frame(ParserNode *node) {
+ Frame frame;
+
+ const char *fileString = node->values["file"].c_str();
+ if (!fileString) {
+ BS_LOG_ERRORLN("<frame> tag without file attribute occurred in \"%s\".", getFileName().c_str());
+ return false;
+ }
+ frame.fileName = _pPackage->getAbsolutePath(fileString);
+ if (frame.fileName.empty()) {
+ BS_LOG_ERRORLN("Could not create absolute path for file specified in <frame> tag in \"%s\": \"%s\".",
+ getFileName().c_str(), fileString);
+ return false;
+ }
+
+ const char *actionString = node->values["action"].c_str();
+ if (actionString)
+ frame.action = actionString;
+
+ const char *hotspotxString = node->values["hotspotx"].c_str();
+ const char *hotspotyString = node->values["hotspoty"].c_str();
+ if ((!hotspotxString && hotspotyString) ||
+ (hotspotxString && !hotspotyString))
+ BS_LOG_WARNINGLN("%s attribute occurred without %s attribute in <frame> tag in \"%s\". Assuming default (\"0\").",
+ hotspotxString ? "hotspotx" : "hotspoty",
+ !hotspotyString ? "hotspoty" : "hotspotx",
+ getFileName().c_str());
+
+ frame.hotspotX = 0;
+ if (hotspotxString && !parseIntegerKey(hotspotxString, 1, &frame.hotspotX))
+ BS_LOG_WARNINGLN("Illegal hotspotx value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ hotspotxString, getFileName().c_str(), frame.hotspotX);
+
+ frame.hotspotY = 0;
+ if (hotspotyString && !parseIntegerKey(hotspotyString, 1, &frame.hotspotY))
+ BS_LOG_WARNINGLN("Illegal hotspoty value (\"%s\") in frame tag in \"%s\". Assuming default (\"%s\").",
+ hotspotyString, getFileName().c_str(), frame.hotspotY);
+
+ Common::String flipVString = node->values["flipv"];
+ if (!flipVString.empty()) {
+ if (!parseBooleanKey(flipVString, frame.flipV)) {
+ BS_LOG_WARNINGLN("Illegal flipv value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ flipVString.c_str(), getFileName().c_str());
+ frame.flipV = false;
+ }
+ } else
+ frame.flipV = false;
+
+ Common::String flipHString = node->values["fliph"];
+ if (!flipHString.empty()) {
+ if (!parseBooleanKey(flipVString, frame.flipV)) {
+ BS_LOG_WARNINGLN("Illegal fliph value (\"%s\") in <frame> tag in \"%s\". Assuming default (\"false\").",
+ flipHString.c_str(), getFileName().c_str());
+ frame.flipH = false;
+ }
+ } else
+ frame.flipH = false;
+
+ _frames.push_back(frame);
+ return true;
+}
+
+AnimationResource::~AnimationResource() {
+}
+
+bool AnimationResource::precacheAllFrames() const {
+ Common::Array<Frame>::const_iterator iter = _frames.begin();
+ for (; iter != _frames.end(); ++iter) {
+ if (!Kernel::GetInstance()->GetResourceManager()->PrecacheResource((*iter).fileName)) {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", (*iter).fileName.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AnimationResource::computeFeatures() {
+ BS_ASSERT(_frames.size());
+
+ // Alle Features werden als vorhanden angenommen
+ _scalingAllowed = true;
+ _alphaAllowed = true;
+ _colorModulationAllowed = true;
+
+ // Alle Frame durchgehen und alle Features deaktivieren, die auch nur von einem Frame nicht unterstützt werden.
+ Common::Array<Frame>::const_iterator iter = _frames.begin();
+ for (; iter != _frames.end(); ++iter) {
+ BitmapResource *pBitmap;
+ if (!(pBitmap = static_cast<BitmapResource *>(Kernel::GetInstance()->GetResourceManager()->RequestResource((*iter).fileName)))) {
+ BS_LOG_ERRORLN("Could not request \"%s\".", (*iter).fileName.c_str());
+ return false;
+ }
+
+ if (!pBitmap->isScalingAllowed())
+ _scalingAllowed = false;
+ if (!pBitmap->isAlphaAllowed())
+ _alphaAllowed = false;
+ if (!pBitmap->isColorModulationAllowed())
+ _colorModulationAllowed = false;
+
+ pBitmap->release();
+ }
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationresource.h b/engines/sword25/gfx/animationresource.h
new file mode 100644
index 0000000000..da07b55c3b
--- /dev/null
+++ b/engines/sword25/gfx/animationresource.h
@@ -0,0 +1,123 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONRESOURCE_H
+#define SWORD25_ANIMATIONRESOURCE_H
+
+#include "common/xmlparser.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/gfx/animationdescription.h"
+#include "sword25/gfx/animation.h"
+
+namespace Sword25 {
+
+class Kernel;
+class PackageManager;
+
+class AnimationResource : public Resource, public AnimationDescription, public Common::XMLParser {
+public:
+ AnimationResource(const Common::String &filename);
+ virtual ~AnimationResource();
+
+ virtual const Frame &getFrame(uint index) const {
+ BS_ASSERT(index < _frames.size());
+ return _frames[index];
+ }
+ virtual uint getFrameCount() const {
+ return _frames.size();
+ }
+ virtual void unlock() {
+ release();
+ }
+
+ Animation::ANIMATION_TYPES getAnimationType() const {
+ return _animationType;
+ }
+ int getFPS() const {
+ return _FPS;
+ }
+ int getMillisPerFrame() const {
+ return _millisPerFrame;
+ }
+ bool isScalingAllowed() const {
+ return _scalingAllowed;
+ }
+ bool isAlphaAllowed() const {
+ return _alphaAllowed;
+ }
+ bool isColorModulationAllowed() const {
+ return _colorModulationAllowed;
+ }
+ bool isValid() const {
+ return _valid;
+ }
+
+private:
+ bool _valid;
+
+ Common::Array<Frame> _frames;
+
+ PackageManager *_pPackage;
+
+
+ bool computeFeatures();
+ bool precacheAllFrames() const;
+
+ // Parser
+ CUSTOM_XML_PARSER(AnimationResource) {
+ XML_KEY(animation)
+ XML_PROP(fps, true)
+ XML_PROP(type, true)
+
+ XML_KEY(frame)
+ XML_PROP(file, true)
+ XML_PROP(hotspotx, true)
+ XML_PROP(hotspoty, true)
+ XML_PROP(fliph, false)
+ XML_PROP(flipv, false)
+ KEY_END()
+ KEY_END()
+ } PARSER_END()
+
+ bool parseBooleanKey(Common::String s, bool &result);
+
+ // Parser callback methods
+ bool parserCallback_animation(ParserNode *node);
+ bool parserCallback_frame(ParserNode *node);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplate.cpp b/engines/sword25/gfx/animationtemplate.cpp
new file mode 100644
index 0000000000..2019d19565
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.cpp
@@ -0,0 +1,243 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATE"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+
+namespace Sword25 {
+
+uint AnimationTemplate::create(const Common::String &sourceAnimation) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(sourceAnimation);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+uint AnimationTemplate::create(const AnimationTemplate &other) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(other);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+uint AnimationTemplate::create(InputPersistenceBlock &reader, uint handle) {
+ AnimationTemplate *animationTemplatePtr = new AnimationTemplate(reader, handle);
+
+ if (animationTemplatePtr->isValid()) {
+ return AnimationTemplateRegistry::getInstance().resolvePtr(animationTemplatePtr);
+ } else {
+ delete animationTemplatePtr;
+ return 0;
+ }
+}
+
+AnimationTemplate::AnimationTemplate(const Common::String &sourceAnimation) {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::getInstance().registerObject(this);
+
+ _valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ _sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
+
+ // Erfolg signalisieren
+ _valid = (_sourceAnimationPtr != 0);
+}
+
+AnimationTemplate::AnimationTemplate(const AnimationTemplate &other) : AnimationDescription() {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::getInstance().registerObject(this);
+
+ _valid = false;
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt.
+ if (!other._sourceAnimationPtr)
+ return;
+ _sourceAnimationPtr = requestSourceAnimation(other._sourceAnimationPtr->getFileName());
+
+ // Alle Member kopieren.
+ _animationType = other._animationType;
+ _FPS = other._FPS;
+ _millisPerFrame = other._millisPerFrame;
+ _scalingAllowed = other._scalingAllowed;
+ _alphaAllowed = other._alphaAllowed;
+ _colorModulationAllowed = other._colorModulationAllowed;
+ _frames = other._frames;
+ _sourceAnimationPtr = other._sourceAnimationPtr;
+ _valid = other._valid;
+
+ _valid &= (_sourceAnimationPtr != 0);
+}
+
+AnimationTemplate::AnimationTemplate(InputPersistenceBlock &reader, uint handle) {
+ // Objekt registrieren.
+ AnimationTemplateRegistry::getInstance().registerObject(this, handle);
+
+ // Objekt laden.
+ _valid = unpersist(reader);
+}
+
+AnimationResource *AnimationTemplate::requestSourceAnimation(const Common::String &sourceAnimation) const {
+ ResourceManager *RMPtr = Kernel::GetInstance()->GetResourceManager();
+ Resource *resourcePtr;
+ if (NULL == (resourcePtr = RMPtr->RequestResource(sourceAnimation)) || resourcePtr->GetType() != Resource::TYPE_ANIMATION) {
+ BS_LOG_ERRORLN("The resource \"%s\" could not be requested or is has an invalid type. The animation template can't be created.", sourceAnimation.c_str());
+ return 0;
+ }
+ return static_cast<AnimationResource *>(resourcePtr);
+}
+
+AnimationTemplate::~AnimationTemplate() {
+ // Animations-Resource freigeben
+ if (_sourceAnimationPtr) {
+ _sourceAnimationPtr->release();
+ }
+
+ // Objekt deregistrieren
+ AnimationTemplateRegistry::getInstance().deregisterObject(this);
+}
+
+void AnimationTemplate::addFrame(int index) {
+ if (validateSourceIndex(index)) {
+ _frames.push_back(_sourceAnimationPtr->getFrame(index));
+ }
+}
+
+void AnimationTemplate::setFrame(int destIndex, int srcIndex) {
+ if (validateDestIndex(destIndex) && validateSourceIndex(srcIndex)) {
+ _frames[destIndex] = _sourceAnimationPtr->getFrame(srcIndex);
+ }
+}
+
+bool AnimationTemplate::validateSourceIndex(uint index) const {
+ if (index > _sourceAnimationPtr->getFrameCount()) {
+ BS_LOG_WARNINGLN("Tried to insert a frame (\"%d\") that does not exist in the source animation (\"%s\"). Ignoring call.",
+ index, _sourceAnimationPtr->getFileName().c_str());
+ return false;
+ } else
+ return true;
+}
+
+bool AnimationTemplate::validateDestIndex(uint index) const {
+ if (index > _frames.size()) {
+ BS_LOG_WARNINGLN("Tried to change a nonexistent frame (\"%d\") in a template animation. Ignoring call.",
+ index);
+ return false;
+ } else
+ return true;
+}
+
+void AnimationTemplate::setFPS(int FPS) {
+ _FPS = FPS;
+ _millisPerFrame = 1000000 / _FPS;
+}
+
+bool AnimationTemplate::persist(OutputPersistenceBlock &writer) {
+ bool Result = true;
+
+ // Parent persistieren.
+ Result &= AnimationDescription::persist(writer);
+
+ // Frameanzahl schreiben.
+ writer.write(_frames.size());
+
+ // Frames einzeln persistieren.
+ Common::Array<const Frame>::const_iterator Iter = _frames.begin();
+ while (Iter != _frames.end()) {
+ writer.write(Iter->hotspotX);
+ writer.write(Iter->hotspotY);
+ writer.write(Iter->flipV);
+ writer.write(Iter->flipH);
+ writer.write(Iter->fileName);
+ writer.write(Iter->action);
+ ++Iter;
+ }
+
+ // Restliche Member persistieren.
+ writer.write(_sourceAnimationPtr->getFileName());
+ writer.write(_valid);
+
+ return Result;
+}
+
+bool AnimationTemplate::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Parent wieder herstellen.
+ result &= AnimationDescription::unpersist(reader);
+
+ // Frameanzahl lesen.
+ uint frameCount;
+ reader.read(frameCount);
+
+ // Frames einzeln wieder herstellen.
+ for (uint i = 0; i < frameCount; ++i) {
+ Frame frame;
+ reader.read(frame.hotspotX);
+ reader.read(frame.hotspotY);
+ reader.read(frame.flipV);
+ reader.read(frame.flipH);
+ reader.read(frame.fileName);
+ reader.read(frame.action);
+
+ _frames.push_back(frame);
+ }
+
+ // Die Animations-Resource wird für die gesamte Lebensdauer des Objektes gelockt
+ Common::String sourceAnimation;
+ reader.read(sourceAnimation);
+ _sourceAnimationPtr = requestSourceAnimation(sourceAnimation);
+
+ reader.read(_valid);
+
+ return _sourceAnimationPtr && reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationtemplate.h b/engines/sword25/gfx/animationtemplate.h
new file mode 100644
index 0000000000..294f249f81
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplate.h
@@ -0,0 +1,125 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATION_TEMPLATE_H
+#define SWORD25_ANIMATION_TEMPLATE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/animationdescription.h"
+
+namespace Sword25 {
+
+class AnimationResource;
+
+class AnimationTemplate : public AnimationDescription {
+public:
+ static uint create(const Common::String &sourceAnimation);
+ static uint create(const AnimationTemplate &other);
+ static uint create(InputPersistenceBlock &reader, uint handle);
+ AnimationTemplate *resolveHandle(uint handle) const;
+
+private:
+ AnimationTemplate(const Common::String &sourceAnimation);
+ AnimationTemplate(const AnimationTemplate &other);
+ AnimationTemplate(InputPersistenceBlock &reader, uint handle);
+
+public:
+ ~AnimationTemplate();
+
+ virtual const Frame &getFrame(uint index) const {
+ BS_ASSERT(index < _frames.size());
+ return _frames[index];
+ }
+ virtual uint getFrameCount() const {
+ return _frames.size();
+ }
+ virtual void unlock() {
+ delete this;
+ }
+
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ @brief Fügt einen neuen Frame zur Animation hinzu.
+
+ Der Frame wird an das Ende der Animation angehängt.
+
+ @param Index der Index des Frames in der Quellanimation
+ */
+ void addFrame(int index);
+
+ /**
+ @brief Ändert einen bereits in der Animation vorhandenen Frame.
+ @param DestIndex der Index des Frames der überschrieben werden soll
+ @param SrcIndex der Index des einzufügenden Frames in der Quellanimation
+ */
+ void setFrame(int destIndex, int srcIndex);
+
+ /**
+ @brief Setzt den Animationstyp.
+ @param Type der Typ der Animation. Muss aus den enum BS_Animation::ANIMATION_TYPES sein.
+ */
+ void setAnimationType(Animation::ANIMATION_TYPES type) {
+ _animationType = type;
+ }
+
+ /**
+ @brief Setzt die Abspielgeschwindigkeit.
+ @param FPS die Abspielgeschwindigkeit in Frames pro Sekunde.
+ */
+ void setFPS(int FPS);
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ Common::Array<Frame> _frames;
+ AnimationResource *_sourceAnimationPtr;
+ bool _valid;
+
+ AnimationResource *requestSourceAnimation(const Common::String &sourceAnimation) const;
+ bool validateSourceIndex(uint index) const;
+ bool validateDestIndex(uint index) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/animationtemplateregistry.cpp b/engines/sword25/gfx/animationtemplateregistry.cpp
new file mode 100644
index 0000000000..6f4af690c6
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.cpp
@@ -0,0 +1,107 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "ANIMATIONTEMPLATEREGISTRY"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "sword25/gfx/animationtemplate.h"
+
+namespace Sword25 {
+
+Common::ScopedPtr<AnimationTemplateRegistry> AnimationTemplateRegistry::_instancePtr;
+
+void AnimationTemplateRegistry::logErrorLn(const char *message) const {
+ BS_LOG_ERRORLN(message);
+}
+
+void AnimationTemplateRegistry::logWarningLn(const char *message) const {
+ BS_LOG_WARNINGLN(message);
+}
+
+bool AnimationTemplateRegistry::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Das nächste zu vergebene Handle schreiben.
+ writer.write(_nextHandle);
+
+ // Anzahl an BS_AnimationTemplates schreiben.
+ writer.write(_handle2PtrMap.size());
+
+ // Alle BS_AnimationTemplates persistieren.
+ HANDLE2PTR_MAP::const_iterator iter = _handle2PtrMap.begin();
+ while (iter != _handle2PtrMap.end()) {
+ // Handle persistieren.
+ writer.write(iter->_key);
+
+ // Objekt persistieren.
+ result &= iter->_value->persist(writer);
+
+ ++iter;
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool AnimationTemplateRegistry::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Das nächste zu vergebene Handle wieder herstellen.
+ reader.read(_nextHandle);
+
+ // Alle vorhandenen BS_AnimationTemplates zerstören.
+ while (!_handle2PtrMap.empty())
+ delete _handle2PtrMap.begin()->_value;
+
+ // Anzahl an BS_AnimationTemplates einlesen.
+ uint animationTemplateCount;
+ reader.read(animationTemplateCount);
+
+ // Alle gespeicherten BS_AnimationTemplates wieder herstellen.
+ for (uint i = 0; i < animationTemplateCount; ++i) {
+ // Handle lesen.
+ uint handle;
+ reader.read(handle);
+
+ // BS_AnimationTemplate wieder herstellen.
+ result &= (AnimationTemplate::create(reader, handle) != 0);
+ }
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/animationtemplateregistry.h b/engines/sword25/gfx/animationtemplateregistry.h
new file mode 100644
index 0000000000..256cbab8cd
--- /dev/null
+++ b/engines/sword25/gfx/animationtemplateregistry.h
@@ -0,0 +1,68 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ANIMATIONTEMPLATEREGISTRY_H
+#define SWORD25_ANIMATIONTEMPLATEREGISTRY_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/kernel/objectregistry.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+class AnimationTemplate;
+
+class AnimationTemplateRegistry : public ObjectRegistry<AnimationTemplate>, public Persistable {
+public:
+ static AnimationTemplateRegistry &getInstance() {
+ if (!_instancePtr.get())
+ _instancePtr.reset(new AnimationTemplateRegistry);
+ return *_instancePtr.get();
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+
+ static Common::ScopedPtr<AnimationTemplateRegistry> _instancePtr;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/bitmap.cpp b/engines/sword25/gfx/bitmap.cpp
new file mode 100644
index 0000000000..3a6ffb4a98
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.cpp
@@ -0,0 +1,215 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/bitmap.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "BITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+Bitmap::Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
+ RenderObject(parentPtr, type, handle),
+ _modulationColor(0xffffffff),
+ _scaleFactorX(1.0f),
+ _scaleFactorY(1.0f),
+ _flipH(false),
+ _flipV(false) {
+}
+
+// -----------------------------------------------------------------------------
+
+Bitmap::~Bitmap() {
+}
+
+// -----------------------------------------------------------------------------
+// Darstellungsart festlegen
+// -----------------------------------------------------------------------------
+
+void Bitmap::setAlpha(int alpha) {
+ if (!isAlphaAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set alpha value on a bitmap that does not support alpha blending. Call was ignored.");
+ return;
+ }
+
+ if (alpha < 0 || alpha > 255) {
+ int oldAlpha = alpha;
+ if (alpha < 0)
+ alpha = 0;
+ if (alpha > 255)
+ alpha = 255;
+ BS_LOG_WARNINGLN("Tried to set an invalid alpha value (%d) on a bitmap. Value was changed to %d.", oldAlpha, alpha);
+
+ return;
+ }
+
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setModulationColor(uint modulationColor) {
+ if (!isColorModulationAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set modulation color of a bitmap that does not support color modulation. Call was ignored.");
+ return;
+ }
+
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setScaleFactor(float scaleFactor) {
+ setScaleFactorX(scaleFactor);
+ setScaleFactorY(scaleFactor);
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setScaleFactorX(float scaleFactorX) {
+ if (!isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorX < 0) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorX != _scaleFactorX) {
+ _scaleFactorX = scaleFactorX;
+ _width = static_cast<int>(_originalWidth * _scaleFactorX);
+ if (_scaleFactorX <= 0.0f)
+ _scaleFactorX = 0.001f;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setScaleFactorY(float scaleFactorY) {
+ if (!isScalingAllowed()) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap that does not support scaling. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorY < 0) {
+ BS_LOG_WARNINGLN("Tried to set scale factor of a bitmap to a negative value. Call was ignored.");
+ return;
+ }
+
+ if (scaleFactorY != _scaleFactorY) {
+ _scaleFactorY = scaleFactorY;
+ _height = static_cast<int>(_originalHeight * scaleFactorY);
+ if (_scaleFactorY <= 0.0f)
+ _scaleFactorY = 0.001f;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setFlipH(bool flipH) {
+ _flipH = flipH;
+ forceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+
+void Bitmap::setFlipV(bool flipV) {
+ _flipV = flipV;
+ forceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool Bitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+ writer.write(_flipH);
+ writer.write(_flipV);
+ writer.write(_scaleFactorX);
+ writer.write(_scaleFactorY);
+ writer.write(_modulationColor);
+ writer.write(_originalWidth);
+ writer.write(_originalHeight);
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool Bitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+ reader.read(_flipH);
+ reader.read(_flipV);
+ reader.read(_scaleFactorX);
+ reader.read(_scaleFactorY);
+ reader.read(_modulationColor);
+ reader.read(_originalWidth);
+ reader.read(_originalHeight);
+
+ forceRefresh();
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/bitmap.h b/engines/sword25/gfx/bitmap.h
new file mode 100644
index 0000000000..a00baf37a4
--- /dev/null
+++ b/engines/sword25/gfx/bitmap.h
@@ -0,0 +1,197 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_BITMAP_H
+#define SWORD25_BITMAP_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class Bitmap : public RenderObject {
+protected:
+ Bitmap(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle = 0);
+
+public:
+
+ virtual ~Bitmap();
+
+ /**
+ @brief Setzt den Alphawert des Bitmaps.
+ @param Alpha der neue Alphawert der Bitmaps (0 = keine Deckung, 255 = volle Deckung).
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Setzt die Modulationfarbe der Bitmaps.
+ @param Color eine 24-Bit Farbe, die die Modulationsfarbe des Bitmaps festlegt.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ void setModulationColor(uint modulationColor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor des Bitmaps.
+ @param ScaleFactor der Faktor um den das Bitmap in beide Richtungen gestreckt werden soll.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactor(float scaleFactor);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der X-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der X-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorX(float scaleFactorX);
+
+ /**
+ @brief Setzt den Skalierungsfaktor der Bitmap auf der Y-Achse.
+ @param ScaleFactor der Faktor um den die Bitmap in Richtungen der Y-Achse gestreckt werden soll. Dieser Wert muss positiv sein.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ void setScaleFactorY(float scaleFactorY);
+
+ /**
+ @brief Legt fest, ob das Bild an der X-Achse gespiegelt werden soll.
+ */
+ void setFlipH(bool flipH);
+
+ /**
+ @brief Legt fest, ob das Bild an der Y-Achse gespiegelt werden soll.
+ */
+ void setFlipV(bool flipV);
+
+ /**
+ @brief Gibt den aktuellen Alphawert des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsAlphaAllowed() true zurückgibt.
+ */
+ int getAlpha() {
+ return _modulationColor >> 24;
+ }
+
+ /**
+ @brief Gibt die aktuelle 24bit RGB Modulationsfarde des Bildes zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsColorModulationAllowed() true zurückgibt.
+ */
+ int getModulationColor() {
+ return _modulationColor & 0x00ffffff;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der X-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorX() const {
+ return _scaleFactorX;
+ }
+
+ /**
+ @brief Gibt den Skalierungsfakter des Bitmaps auf der Y-Achse zurück.
+ @remark Diese Methode darf nur aufgerufen werden, wenn die Methode IsScalingAllowed() true zurückgibt.
+ */
+ float getScaleFactorY() const {
+ return _scaleFactorY;
+ }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der X-Achse gespiegelt angezeigt wird.
+ */
+ bool isFlipH() {
+ return _flipH;
+ }
+
+ /**
+ @brief Gibt zurück, ob das Bild an der Y-Achse gespiegelt angezeigt wird.
+ */
+ bool isFlipV() {
+ return _flipV;
+ }
+
+ // -----------------------------------------------------------------------------
+ // Die folgenden Methoden müssen alle BS_Bitmap-Klassen implementieren
+ // -----------------------------------------------------------------------------
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual uint getPixel(int x, int y) const = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0) = 0;
+
+ virtual bool isScalingAllowed() const = 0;
+ virtual bool isAlphaAllowed() const = 0;
+ virtual bool isColorModulationAllowed() const = 0;
+ virtual bool isSetContentAllowed() const = 0;
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ bool _flipH;
+ bool _flipV;
+ float _scaleFactorX;
+ float _scaleFactorY;
+ uint _modulationColor;
+ int _originalWidth;
+ int _originalHeight;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/bitmapresource.cpp b/engines/sword25/gfx/bitmapresource.cpp
new file mode 100644
index 0000000000..46e6ca77ce
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.cpp
@@ -0,0 +1,68 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/image/imageloader.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "BITMAP"
+
+// Konstruktion / Destruktion
+// --------------------------
+
+BitmapResource::BitmapResource(const Common::String &filename, Image *pImage) :
+ _valid(false),
+ _pImage(pImage),
+ Resource(filename, Resource::TYPE_BITMAP) {
+ _valid = _pImage != 0;
+}
+
+BitmapResource::~BitmapResource() {
+ delete _pImage;
+}
+
+// -----------------------------------------------------------------------------
+
+uint BitmapResource::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _pImage->getWidth());
+ BS_ASSERT(y >= 0 && y < _pImage->getHeight());
+
+ return _pImage->getPixel(x, y);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/bitmapresource.h b/engines/sword25/gfx/bitmapresource.h
new file mode 100644
index 0000000000..1054770e79
--- /dev/null
+++ b/engines/sword25/gfx/bitmapresource.h
@@ -0,0 +1,213 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_BITMAP_RESOURCE_H
+#define SWORD25_BITMAP_RESOURCE_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+#include "sword25/gfx/image/image.h"
+
+namespace Sword25 {
+
+class BitmapResource : public Resource {
+public:
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ BitmapResource(const Common::String &filename, Image *pImage);
+ virtual ~BitmapResource();
+
+ /**
+ @brief Gibt zurück, ob das Objekt einen gültigen Zustand hat.
+ */
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ @brief Gibt die Breite des Bitmaps zurück.
+ */
+ int getWidth() const {
+ BS_ASSERT(_pImage);
+ return _pImage->getWidth();
+ }
+
+ /**
+ @brief Gibt die Höhe des Bitmaps zurück.
+ */
+ int getHeight() const {
+ BS_ASSERT(_pImage);
+ return _pImage->getHeight();
+ }
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein Common::Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ */
+ bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pSrcPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1) {
+ BS_ASSERT(_pImage);
+ return _pImage->blit(posX, posY, flipping, pSrcPartRect, color, width, height);
+ }
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein Common::Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Ein Aufruf dieser Methode ist nur gestattet, wenn IsFillingAllowed() true zurückgibt.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) {
+ BS_ASSERT(_pImage);
+ return _pImage->fill(pFillRect, color);
+ }
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ uint getPixel(int x, int y) const;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ bool isBlitTarget() {
+ BS_ASSERT(_pImage);
+ return _pImage->isBlitTarget();
+ }
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ bool isScalingAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isScalingAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ bool isFillingAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isFillingAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ bool isAlphaAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isAlphaAllowed();
+ }
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ bool isColorModulationAllowed() {
+ BS_ASSERT(_pImage);
+ return _pImage->isColorModulationAllowed();
+ }
+
+private:
+ Image *_pImage;
+ bool _valid;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/dynamicbitmap.cpp b/engines/sword25/gfx/dynamicbitmap.cpp
new file mode 100644
index 0000000000..91d46e99f4
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.cpp
@@ -0,0 +1,193 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/dynamicbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "DYNAMICBITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+DynamicBitmap::DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height) :
+ Bitmap(parentPtr, TYPE_DYNAMICBITMAP) {
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess) return;
+
+ _initSuccess = createRenderedImage(width, height);
+}
+
+// -----------------------------------------------------------------------------
+
+DynamicBitmap::DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ Bitmap(parentPtr, TYPE_DYNAMICBITMAP, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::createRenderedImage(uint width, uint height) {
+ // RenderedImage mit den gewünschten Maßen erstellen
+ bool result = false;
+ _image.reset(new RenderedImage(width, height, result));
+
+ _originalWidth = _width = width;
+ _originalHeight = _height = height;
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+DynamicBitmap::~DynamicBitmap() {
+}
+
+// -----------------------------------------------------------------------------
+
+uint DynamicBitmap::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ return _image->getPixel(x, y);
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::doRender() {
+ // Framebufferobjekt holen
+ GraphicEngine *pGfx = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGfx);
+
+ // Bitmap zeichnen
+ bool result;
+ if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
+ result = _image->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ } else {
+ result = _image->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ return _image->setContent(pixeldata, size, offset, stride);
+}
+
+// -----------------------------------------------------------------------------
+// Auskunftsmethoden
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::isScalingAllowed() const {
+ return _image->isScalingAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::isAlphaAllowed() const {
+ return _image->isAlphaAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::isColorModulationAllowed() const {
+ return _image->isColorModulationAllowed();
+}
+
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::isSetContentAllowed() const {
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool DynamicBitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= Bitmap::persist(writer);
+
+ // Bilddaten werden nicht gespeichert. Dies ist auch nicht weiter von bedeutung, da BS_DynamicBitmap nur vom Videoplayer benutzt wird.
+ // Während ein Video abläuft kann niemals gespeichert werden. BS_DynamicBitmap kann nur der Vollständigkeit halber persistiert werden.
+ BS_LOG_WARNINGLN("Persisting a BS_DynamicBitmap. Bitmap content is not persisted.");
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool DynamicBitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= Bitmap::unpersist(reader);
+
+ // Ein RenderedImage mit den gespeicherten Maßen erstellen.
+ result &= createRenderedImage(_width, _height);
+
+ // Bilddaten werden nicht gespeichert (s.o.).
+ BS_LOG_WARNINGLN("Unpersisting a BS_DynamicBitmap. Bitmap contents are missing.");
+
+ // Bild mit durchsichtigen Bilddaten initialisieren.
+ byte *transparentImageData = (byte *)calloc(_width * _height * 4, 1);
+ _image->setContent(transparentImageData, _width * _height);
+ free(transparentImageData);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/dynamicbitmap.h b/engines/sword25/gfx/dynamicbitmap.h
new file mode 100644
index 0000000000..a02769fb57
--- /dev/null
+++ b/engines/sword25/gfx/dynamicbitmap.h
@@ -0,0 +1,87 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_DYNAMIC_BITMAP_H
+#define SWORD25_DYNAMIC_BITMAP_H
+
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class DynamicBitmap : public Bitmap {
+ friend class RenderObject;
+
+public:
+ virtual ~DynamicBitmap();
+
+ virtual uint getPixel(int x, int y) const;
+
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const;
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ DynamicBitmap(RenderObjectPtr<RenderObject> parentPtr, uint width, uint height);
+ DynamicBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+ bool createRenderedImage(uint width, uint height);
+
+ Common::ScopedPtr<RenderedImage> _image;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp
new file mode 100644
index 0000000000..9f23133a71
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -0,0 +1,153 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "FONTRESOURCE"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/string.h"
+#include "sword25/package/packagemanager.h"
+
+#include "sword25/gfx/fontresource.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+static const uint DEFAULT_LINEHEIGHT = 20;
+static const uint DEFAULT_GAPWIDTH = 1;
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+FontResource::FontResource(Kernel *pKernel, const Common::String &FileName) :
+ _pKernel(pKernel),
+ _Valid(false),
+ Resource(FileName, Resource::TYPE_FONT),
+ Common::XMLParser() {
+
+ // Get a pointer to the package manager
+ BS_ASSERT(_pKernel);
+ PackageManager *pPackage = static_cast<PackageManager *>(_pKernel->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Load the contents of the file
+ uint fileSize;
+ char *xmlData = pPackage->getXmlFile(getFileName(), &fileSize);
+ if (!xmlData) {
+ BS_LOG_ERRORLN("Could not read \"%s\".", getFileName().c_str());
+ return;
+ }
+
+ // Parse the contents
+ if (!loadBuffer((const byte *)xmlData, fileSize))
+ return;
+
+ _Valid = parse();
+ close();
+ free(xmlData);
+}
+
+// -----------------------------------------------------------------------------
+
+bool FontResource::parserCallback_font(ParserNode *node) {
+ // Get the attributes of the font
+ Common::String bitmapFilename = node->values["bitmap"];
+
+ if (!parseIntegerKey(node->values["lineheight"].c_str(), 1, &_LineHeight)) {
+ BS_LOG_WARNINGLN("Illegal or missing lineheight attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_LINEHEIGHT);
+ _LineHeight = DEFAULT_LINEHEIGHT;
+ }
+
+ if (!parseIntegerKey(node->values["gap"].c_str(), 1, &_GapWidth)) {
+ BS_LOG_WARNINGLN("Illegal or missing gap attribute in <font> tag in \"%s\". Assuming default (\"%d\").",
+ getFileName().c_str(), DEFAULT_GAPWIDTH);
+ _GapWidth = DEFAULT_GAPWIDTH;
+ }
+
+ // Get a reference to the package manager
+ BS_ASSERT(_pKernel);
+ PackageManager *pPackage = static_cast<PackageManager *>(_pKernel->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Get the full path and filename for the bitmap resource
+ _BitmapFileName = pPackage->getAbsolutePath(bitmapFilename);
+ if (_BitmapFileName == "") {
+ BS_LOG_ERRORLN("Image file \"%s\" was specified in <font> tag of \"%s\" but could not be found.",
+ _BitmapFileName.c_str(), getFileName().c_str());
+ }
+
+ // Pre-cache the resource
+ if (!_pKernel->GetResourceManager()->PrecacheResource(_BitmapFileName)) {
+ BS_LOG_ERRORLN("Could not precache \"%s\".", _BitmapFileName.c_str());
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool FontResource::parserCallback_character(ParserNode *node) {
+ // Get the attributes of the character
+ int charCode, top, left, right, bottom;
+
+ if (!parseIntegerKey(node->values["code"].c_str(), 1, &charCode) || (charCode < 0) || (charCode >= 256)) {
+ return parserError("Illegal or missing code attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+
+ if (!parseIntegerKey(node->values["top"].c_str(), 1, &top) || (top < 0)) {
+ return parserError("Illegal or missing top attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["left"].c_str(), 1, &left) || (left < 0)) {
+ return parserError("Illegal or missing left attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["right"].c_str(), 1, &right) || (right < 0)) {
+ return parserError("Illegal or missing right attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+ if (!parseIntegerKey(node->values["bottom"].c_str(), 1, &bottom) || (bottom < 0)) {
+ return parserError("Illegal or missing bottom attribute in <character> tag in \"%s\".", getFileName().c_str());
+ }
+
+ this->_CharacterRects[charCode] = Common::Rect(left, top, right, bottom);
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/fontresource.h b/engines/sword25/gfx/fontresource.h
new file mode 100644
index 0000000000..cfb0251084
--- /dev/null
+++ b/engines/sword25/gfx/fontresource.h
@@ -0,0 +1,154 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_FONTRESOURCE_H
+#define SWORD25_FONTRESOURCE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "common/xmlparser.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resource.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward declarations
+// -----------------------------------------------------------------------------
+
+class Kernel;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class FontResource : public Resource, Common::XMLParser {
+public:
+ /**
+ @brief Erzeugt eine neues Exemplar von BS_FontResource
+ @param pKernel ein Pointer auf den Kernel
+ @param FileName der Dateiname der zu ladenen Resource
+ @remark Wenn der Konstruktor erfolgreich ausgeführt werden konnte gibt die Methode IsValid true zurück.
+ */
+ FontResource(Kernel *pKernel, const Common::String &FileName);
+
+ /**
+ @brief Gibt true zurück, wenn das Objekt korrekt initialisiert wurde.
+
+ Diese Methode kann dazu benutzt werden um festzustellen, ob der Konstruktor erfolgreich ausgeführt wurde.
+ */
+ bool IsValid() const {
+ return _Valid;
+ }
+
+ /**
+ @brief Gibt die Zeilenhöhe des Fonts in Pixeln zurück.
+
+ Die Zeilenhöhe ist der Wert, der zur Y-Koordinate addiert wird, wenn ein Zeilenumbruch auftritt.
+ */
+ int GetLineHeight() const {
+ return _LineHeight;
+ }
+
+ /**
+ @brief Gibt den Buchstabenabstand der Fonts in Pixeln zurück.
+
+ Der Buchstabenabstand ist der Wert, der zwischen zwei Buchstaben freigelassen wird.
+ */
+ int GetGapWidth() const {
+ return _GapWidth;
+ }
+
+ /**
+ @brief Gibt das Bounding-Rect eines Zeichens auf der Charactermap zurück.
+ @param Character der ASCII-Code des Zeichens
+ @return Das Bounding-Rect des übergebenen Zeichens auf der Charactermap.
+ */
+ const Common::Rect &GetCharacterRect(int Character) const {
+ BS_ASSERT(Character >= 0 && Character < 256);
+ return _CharacterRects[Character];
+ }
+
+ /**
+ @brief Gibt den Dateinamen der Charactermap zurück.
+ */
+ const Common::String &GetCharactermapFileName() const {
+ return _BitmapFileName;
+ }
+
+private:
+ Kernel *_pKernel;
+ bool _Valid;
+ Common::String _BitmapFileName;
+ int _LineHeight;
+ int _GapWidth;
+ Common::Rect _CharacterRects[256];
+
+ // Parser
+ CUSTOM_XML_PARSER(FontResource) {
+ XML_KEY(font)
+ XML_PROP(bitmap, true)
+ XML_PROP(lineheight, false)
+ XML_PROP(gap, false)
+
+ XML_KEY(character)
+ XML_PROP(code, true)
+ XML_PROP(left, true)
+ XML_PROP(top, true)
+ XML_PROP(right, true)
+ XML_PROP(bottom, true)
+ KEY_END()
+ KEY_END()
+ } PARSER_END()
+
+ // Parser callback methods
+ bool parserCallback_font(ParserNode *node);
+ bool parserCallback_character(ParserNode *node);
+
+ // -----------------------------------------------------------------------------
+ // Hilfsmethoden
+ // -----------------------------------------------------------------------------
+
+// bool _ParseXMLDocument(const Common::String &FileName, TiXmlDocument &Doc) const;
+// bool _ParseFontTag(TiXmlElement &Tag, Common::String &BitmapFileName, int &LineHeight, int &GapWidth) const;
+// bool _ParseCharacterTag(TiXmlElement &Tag, int &Code, Common::Rect &Rect) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/framecounter.cpp b/engines/sword25/gfx/framecounter.cpp
new file mode 100644
index 0000000000..15bc7d00ea
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.cpp
@@ -0,0 +1,68 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "common/system.h"
+#include "sword25/gfx/framecounter.h"
+
+namespace Sword25 {
+
+Framecounter::Framecounter(int UpdateFrequency) :
+ m_FPS(0),
+ m_FPSCount(0),
+ m_LastUpdateTime(-1) {
+ SetUpdateFrequency(UpdateFrequency);
+}
+
+void Framecounter::Update() {
+ // Aktuellen Systemtimerstand auslesen
+ uint64_t Timer = g_system->getMillis() * 1000;
+
+ // Falls m_LastUpdateTime == -1 ist, wird der Frame-Counter zum ersten Mal aufgerufen und der aktuelle Systemtimer als erster
+ // Messzeitpunkt genommen.
+ if (m_LastUpdateTime == -1)
+ m_LastUpdateTime = Timer;
+ else {
+ // Die Anzahl der Frames im aktuellen Messzeitraum wird erhöht.
+ m_FPSCount++;
+
+ // Falls der Messzeitraum verstrichen ist, wird die durchschnittliche Framerate berechnet und ein neuer Messzeitraum begonnen.
+ if (Timer - m_LastUpdateTime >= m_UpdateDelay) {
+ m_FPS = static_cast<int>((1000000 * (uint64_t)m_FPSCount) / (Timer - m_LastUpdateTime));
+ m_LastUpdateTime = Timer;
+ m_FPSCount = 0;
+ }
+ }
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/framecounter.h b/engines/sword25/gfx/framecounter.h
new file mode 100644
index 0000000000..8a8402a3bb
--- /dev/null
+++ b/engines/sword25/gfx/framecounter.h
@@ -0,0 +1,94 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_FRAMECOUNTER_H
+#define SWORD25_FRAMECOUNTER_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/bs_stdint.h"
+
+namespace Sword25 {
+
+/**
+ * A simple class that implements a frame counter
+ */
+class Framecounter {
+private:
+ enum {
+ DEFAULT_UPDATE_FREQUENCY = 10
+ };
+
+public:
+ /**
+ * Creates a new BS_Framecounter object
+ * @param UpdateFrequency Specifies how often the frame counter should be updated in a sceond.
+ * The default value is 10.
+ */
+ Framecounter(int UpdateFrequency = DEFAULT_UPDATE_FREQUENCY);
+
+ /**
+ * Determines how often the frame counter should be updated in a second.
+ * @param UpdateFrequency Specifies how often the frame counter should be updated in a second.
+ */
+ inline void SetUpdateFrequency(int UpdateFrequency);
+
+ /**
+ * This method must be called once per frame.
+ */
+ void Update();
+
+ /**
+ * Returns the current FPS value.
+ */
+ int GetFPS() const {
+ return m_FPS;
+ }
+
+private:
+ int m_FPS;
+ int m_FPSCount;
+ int64_t m_LastUpdateTime;
+ uint64_t m_UpdateDelay;
+};
+
+// Inlines
+void Framecounter::SetUpdateFrequency(int UpdateFrequency) {
+ // Frequency in time (converted to microseconds)
+ m_UpdateDelay = 1000000 / UpdateFrequency;
+}
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine.cpp b/engines/sword25/gfx/graphicengine.cpp
new file mode 100644
index 0000000000..ea0c8c82c5
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+#include "common/system.h"
+
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/gfx/animationresource.h"
+#include "sword25/gfx/fontresource.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/renderobjectmanager.h"
+#include "sword25/gfx/screenshot.h"
+#include "sword25/gfx/image/renderedimage.h"
+#include "sword25/gfx/image/swimage.h"
+#include "sword25/gfx/image/vectorimage.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+
+
+#include "sword25/gfx/graphicengine.h"
+
+namespace Lua {
+extern "C"
+{
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+}
+}
+
+namespace {
+const int BIT_DEPTH = 32;
+const int BACKBUFFER_COUNT = 1;
+const Common::String PNG_EXTENSION(".png");
+const Common::String PNG_S_EXTENSION("_s.png");
+const Common::String ANI_EXTENSION("_ani.xml");
+const Common::String FNT_EXTENSION("_fnt.xml");
+const Common::String SWF_EXTENSION(".swf");
+const Common::String B25S_EXTENSION(".b25s");
+}
+
+
+namespace Sword25 {
+
+using namespace Lua;
+
+static const uint FRAMETIME_SAMPLE_COUNT = 5; // Anzahl der Framezeiten über die, die Framezeit gemittelt wird
+
+GraphicEngine::GraphicEngine(Kernel *pKernel) :
+ m_Width(0),
+ m_Height(0),
+ m_BitDepth(0),
+ m_Windowed(0),
+ m_LastTimeStamp((uint64) - 1), // max. BS_INT64 um beim ersten Aufruf von _UpdateLastFrameDuration() einen Reset zu erzwingen
+ m_LastFrameDuration(0),
+ m_TimerActive(true),
+ m_FrameTimeSampleSlot(0),
+ m_RepaintedPixels(0),
+ _thumbnail(NULL),
+ ResourceService(pKernel) {
+ m_FrameTimeSamples.resize(FRAMETIME_SAMPLE_COUNT);
+
+ if (!RegisterScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+GraphicEngine::~GraphicEngine() {
+ _backSurface.free();
+ _frameBuffer.free();
+ delete _thumbnail;
+}
+
+Service *GraphicEngine_CreateObject(Kernel *pKernel) {
+ return new GraphicEngine(pKernel);
+}
+
+bool GraphicEngine::Init(int Width, int Height, int BitDepth, int BackbufferCount, bool Windowed) {
+ // Warnung ausgeben, wenn eine nicht unterstützte Bittiefe gewählt wurde.
+ if (BitDepth != BIT_DEPTH) {
+ BS_LOG_WARNINGLN("Can't use a bit depth of %d (not supported). Falling back to %d.", BitDepth, BIT_DEPTH);
+ m_BitDepth = BIT_DEPTH;
+ }
+
+ // Warnung ausgeben, wenn nicht genau ein Backbuffer gewählt wurde.
+ if (BackbufferCount != BACKBUFFER_COUNT) {
+ BS_LOG_WARNINGLN("Can't use %d backbuffers (not supported). Falling back to %d.", BackbufferCount, BACKBUFFER_COUNT);
+ BackbufferCount = BACKBUFFER_COUNT;
+ }
+
+ // Parameter in lokale Variablen kopieren
+ m_Width = Width;
+ m_Height = Height;
+ m_BitDepth = BitDepth;
+ m_Windowed = Windowed;
+ m_ScreenRect.left = 0;
+ m_ScreenRect.top = 0;
+ m_ScreenRect.right = m_Width;
+ m_ScreenRect.bottom = m_Height;
+
+ _backSurface.create(Width, Height, 4);
+ _frameBuffer.create(Width, Height, 4);
+
+ // Standardmäßig ist Vsync an.
+ SetVsync(true);
+
+ // Layer-Manager initialisieren.
+ _renderObjectManagerPtr.reset(new RenderObjectManager(Width, Height, BackbufferCount + 1));
+
+ // Hauptpanel erstellen
+ m_MainPanelPtr = _renderObjectManagerPtr->getTreeRoot()->addPanel(Width, Height, BS_ARGB(0, 0, 0, 0));
+ if (!m_MainPanelPtr.isValid()) return false;
+ m_MainPanelPtr->setVisible(true);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::StartFrame(bool UpdateAll) {
+ // Berechnen, wie viel Zeit seit dem letzten Frame vergangen ist.
+ // Dieser Wert kann über GetLastFrameDuration() von Modulen abgefragt werden, die zeitabhängig arbeiten.
+ UpdateLastFrameDuration();
+
+ // Den Layer-Manager auf den nächsten Frame vorbereiten
+ _renderObjectManagerPtr->startFrame();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::EndFrame() {
+ // Scene zeichnen
+ _renderObjectManagerPtr->render();
+
+ // FIXME: The frame buffer surface is only used as the base for creating thumbnails when saving the
+ // game, since the _backSurface is blanked. Currently I'm doing a slightly hacky check and only
+ // copying the back surface if line 50 (the first line after the interface area) is non-blank
+ if (READ_LE_UINT32((byte *)_backSurface.pixels + (_backSurface.pitch * 50)) & 0xffffff) {
+ // Make a copy of the current frame into the frame buffer
+ Common::copy((byte *)_backSurface.pixels, (byte *)_backSurface.pixels +
+ (_backSurface.pitch * _backSurface.h), (byte *)_frameBuffer.pixels);
+ }
+
+ g_system->updateScreen();
+
+ // Debug-Lines zeichnen
+ if (!m_DebugLines.empty()) {
+#if 0
+ glEnable(GL_LINE_SMOOTH);
+ glBegin(GL_LINES);
+
+ Common::Array<DebugLine>::const_iterator iter = m_DebugLines.begin();
+ for (; iter != m_DebugLines.end(); ++iter) {
+ const uint &Color = (*iter).Color;
+ const BS_Vertex &Start = (*iter).Start;
+ const BS_Vertex &End = (*iter).End;
+
+ glColor4ub((Color >> 16) & 0xff, (Color >> 8) & 0xff, Color & 0xff, Color >> 24);
+ glVertex2d(Start.X, Start.Y);
+ glVertex2d(End.X, End.Y);
+ }
+
+ glEnd();
+ glDisable(GL_LINE_SMOOTH);
+#endif
+
+ warning("STUB: Drawing debug lines");
+
+ m_DebugLines.clear();
+ }
+
+ // Framecounter aktualisieren
+ m_FPSCounter.Update();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Panel> GraphicEngine::GetMainPanel() {
+ return m_MainPanelPtr;
+}
+
+// -----------------------------------------------------------------------------
+
+void GraphicEngine::SetVsync(bool Vsync) {
+ warning("STUB: SetVsync(%d)", Vsync);
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::GetVsync() const {
+ warning("STUB: GetVsync()");
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::fill(const Common::Rect *fillRectPtr, uint color) {
+ Common::Rect rect(m_Width - 1, m_Height - 1);
+
+ if (fillRectPtr) {
+ rect = *fillRectPtr;
+ }
+
+ if (fillRectPtr->width() > 0 && fillRectPtr->height() > 0) {
+ _backSurface.fillRect(rect, color);
+ g_system->copyRectToScreen((byte *)_backSurface.getBasePtr(fillRectPtr->left, fillRectPtr->top), _backSurface.pitch, fillRectPtr->left, fillRectPtr->top, fillRectPtr->width(), fillRectPtr->height());
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+Graphics::Surface *GraphicEngine::GetScreenshot() {
+ return &_frameBuffer;
+}
+
+// -----------------------------------------------------------------------------
+// RESOURCE MANAGING
+// -----------------------------------------------------------------------------
+
+Resource *GraphicEngine::loadResource(const Common::String &FileName) {
+ BS_ASSERT(canLoadResource(FileName));
+
+ // Bild für den Softwarebuffer laden
+ if (FileName.hasSuffix(PNG_S_EXTENSION)) {
+ bool Result = false;
+ SWImage *pImage = new SWImage(FileName, Result);
+ if (!Result) {
+ delete pImage;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+ // Sprite-Bild laden
+ if (FileName.hasSuffix(PNG_EXTENSION) || FileName.hasSuffix(B25S_EXTENSION)) {
+ bool Result = false;
+ RenderedImage *pImage = new RenderedImage(FileName, Result);
+ if (!Result) {
+ delete pImage;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ return 0;
+ }
+
+ return pResource;
+ }
+
+
+ // Vectorgraphik laden
+ if (FileName.hasSuffix(SWF_EXTENSION)) {
+ debug(2, "VectorImage: %s", FileName.c_str());
+
+ // Pointer auf Package-Manager holen
+ PackageManager *pPackage = Kernel::GetInstance()->GetPackage();
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ byte *pFileData;
+ uint FileSize;
+ if (!(pFileData = static_cast<byte *>(pPackage->getFile(FileName, &FileSize)))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", FileName.c_str());
+ return 0;
+ }
+
+ bool Result = false;
+ VectorImage *pImage = new VectorImage(pFileData, FileSize, Result, FileName);
+ if (!Result) {
+ delete pImage;
+ delete [] pFileData;
+ return 0;
+ }
+
+ BitmapResource *pResource = new BitmapResource(FileName, pImage);
+ if (!pResource->isValid()) {
+ delete pResource;
+ delete[] pFileData;
+ return 0;
+ }
+
+ delete[] pFileData;
+ return pResource;
+ }
+
+ // Animation laden
+ if (FileName.hasSuffix(ANI_EXTENSION)) {
+ AnimationResource *pResource = new AnimationResource(FileName);
+ if (pResource->isValid())
+ return pResource;
+ else {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ // Font laden
+ if (FileName.hasSuffix(FNT_EXTENSION)) {
+ FontResource *pResource = new FontResource(Kernel::GetInstance(), FileName);
+ if (pResource->IsValid())
+ return pResource;
+ else {
+ delete pResource;
+ return 0;
+ }
+ }
+
+ BS_LOG_ERRORLN("Service cannot load \"%s\".", FileName.c_str());
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::canLoadResource(const Common::String &FileName) {
+ return FileName.hasSuffix(PNG_EXTENSION) ||
+ FileName.hasSuffix(ANI_EXTENSION) ||
+ FileName.hasSuffix(FNT_EXTENSION) ||
+ FileName.hasSuffix(SWF_EXTENSION) ||
+ FileName.hasSuffix(B25S_EXTENSION);
+}
+
+
+// -----------------------------------------------------------------------------
+// DEBUGGING
+// -----------------------------------------------------------------------------
+
+void GraphicEngine::DrawDebugLine(const Vertex &Start, const Vertex &End, uint Color) {
+ m_DebugLines.push_back(DebugLine(Start, End, Color));
+}
+
+void GraphicEngine::UpdateLastFrameDuration() {
+ // Aktuelle Zeit holen
+ uint64_t CurrentTime = Kernel::GetInstance()->GetMicroTicks();
+
+ // Verstrichene Zeit seit letztem Frame berechnen und zu große Zeitsprünge ( > 250 msek.) unterbinden
+ // (kann vorkommen bei geladenen Spielständen, während des Debuggings oder Hardwareungenauigkeiten)
+ m_FrameTimeSamples[m_FrameTimeSampleSlot] = static_cast<uint>(CurrentTime - m_LastTimeStamp);
+ if (m_FrameTimeSamples[m_FrameTimeSampleSlot] > 250000) m_FrameTimeSamples[m_FrameTimeSampleSlot] = 250000;
+ m_FrameTimeSampleSlot = (m_FrameTimeSampleSlot + 1) % FRAMETIME_SAMPLE_COUNT;
+
+ // Die Framezeit wird über mehrere Frames gemittelt um Ausreisser zu eliminieren
+ Common::Array<uint>::const_iterator it = m_FrameTimeSamples.begin();
+ uint Sum = *it;
+ for (it++; it != m_FrameTimeSamples.end(); it++) Sum += *it;
+ m_LastFrameDuration = Sum / FRAMETIME_SAMPLE_COUNT;
+
+ // _LastTimeStamp auf die Zeit des aktuellen Frames setzen
+ m_LastTimeStamp = CurrentTime;
+}
+
+namespace {
+bool DoSaveScreenshot(GraphicEngine &GraphicEngine, const Common::String &Filename) {
+ Graphics::Surface *data = GraphicEngine.GetScreenshot();
+ if (!data) {
+ BS_LOG_ERRORLN("Call to GetScreenshot() failed. Cannot save screenshot.");
+ return false;
+ }
+
+ Common::FSNode f(Filename);
+ Common::WriteStream *stream = f.createWriteStream();
+ if (!stream) {
+ BS_LOG_ERRORLN("Call to GetScreenshot() failed. Cannot save screenshot.");
+ return false;
+ }
+
+ bool result = Screenshot::SaveToFile(data, stream);
+ delete stream;
+
+ return result;
+}
+}
+
+bool GraphicEngine::SaveScreenshot(const Common::String &Filename) {
+ return DoSaveScreenshot(*this, Filename);
+}
+
+bool GraphicEngine::SaveThumbnailScreenshot(const Common::String &Filename) {
+ // Note: In ScumMVM, rather than saivng the thumbnail to a file, we store it in memory
+ // until needed when creating savegame files
+ delete _thumbnail;
+ _thumbnail = Screenshot::createThumbnail(&_frameBuffer);
+ return true;
+}
+
+void GraphicEngine::ARGBColorToLuaColor(lua_State *L, uint Color) {
+ lua_Number Components[4] = {
+ (Color >> 16) & 0xff, // Rot
+ (Color >> 8) & 0xff, // Grün
+ Color & 0xff, // Blau
+ Color >> 24, // Alpha
+ };
+
+ lua_newtable(L);
+
+ for (uint i = 1; i <= 4; i++) {
+ lua_pushnumber(L, i);
+ lua_pushnumber(L, Components[i - 1]);
+ lua_settable(L, -3);
+ }
+}
+
+uint GraphicEngine::LuaColorToARGBColor(lua_State *L, int StackIndex) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Sicherstellen, dass wir wirklich eine Tabelle betrachten
+ luaL_checktype(L, StackIndex, LUA_TTABLE);
+ // Größe der Tabelle auslesen
+ uint n = luaL_getn(L, StackIndex);
+ // RGB oder RGBA Farben werden unterstützt und sonst keine
+ if (n != 3 && n != 4) luaL_argcheck(L, 0, StackIndex, "at least 3 of the 4 color components have to be specified");
+
+ // Rote Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 1);
+ uint Red = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Red >= 256) luaL_argcheck(L, 0, StackIndex, "red color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Grüne Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 2);
+ uint Green = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Green >= 256) luaL_argcheck(L, 0, StackIndex, "green color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Blaue Farbkomponente auslesen
+ lua_rawgeti(L, StackIndex, 3);
+ uint Blue = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Blue >= 256) luaL_argcheck(L, 0, StackIndex, "blue color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+
+ // Alpha Farbkomponente auslesen
+ uint Alpha = 0xff;
+ if (n == 4) {
+ lua_rawgeti(L, StackIndex, 4);
+ Alpha = static_cast<uint>(lua_tonumber(L, -1));
+ if (!lua_isnumber(L, -1) || Alpha >= 256) luaL_argcheck(L, 0, StackIndex, "alpha color component must be an integer between 0 and 255");
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return (Alpha << 24) | (Red << 16) | (Green << 8) | Blue;
+}
+
+bool GraphicEngine::persist(OutputPersistenceBlock &writer) {
+ writer.write(m_TimerActive);
+
+ bool result = _renderObjectManagerPtr->persist(writer);
+
+ return result;
+}
+
+bool GraphicEngine::unpersist(InputPersistenceBlock &reader) {
+ reader.read(m_TimerActive);
+ _renderObjectManagerPtr->unpersist(reader);
+
+ return reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/graphicengine.h b/engines/sword25/gfx/graphicengine.h
new file mode 100644
index 0000000000..019f5eec4c
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine.h
@@ -0,0 +1,398 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_GraphicEngine
+ * ----------------
+ * This the graphics engine interface.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_GRAPHICENGINE_H
+#define SWORD25_GRAPHICENGINE_H
+
+// Includes
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/kernel/resservice.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/gfx/framecounter.h"
+#include "sword25/gfx/renderobjectptr.h"
+#include "sword25/math/vertex.h"
+
+namespace Sword25 {
+
+class Kernel;
+class Image;
+class Panel;
+class Screenshot;
+class RenderObjectManager;
+
+// Typen
+typedef uint BS_COLOR;
+
+// Makros
+#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
+#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
+
+/**
+ @brief Dies ist das Graphik-Engine Interface, dass alle Methoden und Klassen enthält, die eine Graphik-Engine implementieren muss.
+
+ Hier sind nur wenige Rumpffunktionen realisiert, wie z.B. das Abfragen der Parameter des Ausgabepuffers.
+ Die Hauptfunktionen muss eine Implementation dieses Inferfaces stellen.<br>
+ Die bisher einzige Implementation ist BS_DDrawGfx.
+*/
+
+class GraphicEngine : public ResourceService, public Persistable {
+public:
+ // Enums
+ // -----
+
+ // Colour formats
+ //
+ /**
+ * The colour format used by the engine
+ */
+ enum COLOR_FORMATS {
+ /// Undefined/unknown colour format
+ CF_UNKNOWN = 0,
+ /**
+ * 24-bit colour format (R8G8B8)
+ */
+ CF_RGB24,
+ /**
+ * 32-bit colour format (A8R8G8B8) (little endian)
+ */
+ CF_ARGB32,
+ /**
+ 32-bit colour format (A8B8G8R8) (little endian)
+ */
+ CF_ABGR32
+ };
+
+ // Constructor
+ // -----------
+ GraphicEngine(Kernel *pKernel);
+ ~GraphicEngine();
+
+ // Interface
+ // ---------
+
+ /**
+ * Initialises the graphics engine and sets the screen mode. Returns true if initialisation failed.
+ * Notes: This method should be called immediately after the initialisation of all services.
+ *
+ * @param Height The height of the output buffer in pixels. The default value is 600
+ * @param BitDepth The bit depth of the desired output buffer in bits. The default value is 16
+ * @param BackbufferCount The number of back buffers to be created. The default value is 2
+ * @param Windowed Indicates whether the engine is to run in windowed mode.
+ */
+ bool Init(int Width = 800, int Height = 600, int BitDepth = 16, int BackbufferCount = 2, bool Windowed = false);
+
+ /**
+ * Begins rendering a new frame.
+ * Notes: This method must be called at the beginning of the main loop, before any rendering methods are used.
+ * Notes: Implementations of this method must call _UpdateLastFrameDuration()
+ * @param UpdateAll Specifies whether the renderer should redraw everything on the next frame.
+ * This feature can be useful if the renderer with Dirty Rectangles works, but sometimes the client may
+ */
+ bool StartFrame(bool UpdateAll = false);
+
+ /**
+ * Ends the rendering of a frame and draws it on the screen.
+ *
+ * This method must be at the end of the main loop. After this call, no further Render method may be called.
+ * This should only be called once for a given previous call to #StartFrame.
+ */
+ bool EndFrame();
+
+ // Debug methods
+
+ /**
+ * Draws a line in the frame buffer
+ *
+ * This method must be called between calls to StartFrame() and EndFrame(), and is intended only for debugging
+ * purposes. The line will only appear for a single frame. If the line is to be shown permanently, it must be
+ * called for every frame.
+ * @param Start The starting point of the line
+ * @param End The ending point of the line
+ * @param Color The colour of the line. The default is BS_RGB (255,255,255) (White)
+ */
+ void DrawDebugLine(const Vertex &Start, const Vertex &End, uint Color = BS_RGB(255, 255, 255));
+
+ /**
+ * Creates a screenshot of the current frame buffer and writes it to a graphic file in PNG format.
+ * Returns true if the screenshot was saved successfully.
+ * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
+ * @param Filename The filename for the screenshot
+ */
+ bool SaveScreenshot(const Common::String &Filename);
+
+ /**
+ * Creates a thumbnail with the dimensions of 200x125. This will not include the top and bottom of the screen..
+ * the interface boards the the image as a 16th of it's original size.
+ * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
+ * The frame buffer must have a resolution of 800x600.
+ * @param Filename The filename for the screenshot
+ */
+ bool SaveThumbnailScreenshot(const Common::String &Filename);
+
+ /**
+ * Reads the current contents of the frame buffer
+ * Notes: This method is for creating screenshots. It is not very optimised. It should only be called
+ * after a call to EndFrame(), and before the next call to StartFrame().
+ * @param Width Returns the width of the frame buffer
+ * @param Height Returns the height of the frame buffer
+ * @param Data Returns the raw data of the frame buffer as an array of 32-bit colour values.
+ */
+ Graphics::Surface *GetScreenshot();
+
+
+ RenderObjectPtr<Panel> GetMainPanel();
+
+ /**
+ * Specifies the time (in microseconds) since the last frame has passed
+ */
+ int GetLastFrameDurationMicro() {
+ if (m_TimerActive) return m_LastFrameDuration;
+ else return 0;
+ }
+
+ /**
+ * Specifies the time (in microseconds) the previous frame took
+ */
+ float GetLastFrameDuration() {
+ if (m_TimerActive) return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
+ else return 0;
+ }
+
+ void StopMainTimer() {
+ m_TimerActive = false;
+ }
+ void ResumeMainTimer() {
+ m_TimerActive = true;
+ }
+ float GetSecondaryFrameDuration() {
+ return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
+ }
+
+ // Accessor methods
+
+ /**
+ * Returns the width of the output buffer in pixels
+ */
+ int GetDisplayWidth() {
+ return m_Width;
+ }
+
+ /**
+ * Returns the height of the output buffer in pixels
+ */
+ int GetDisplayHeight() {
+ return m_Height;
+ }
+
+ /**
+ * Returns the bounding box of the output buffer: (0, 0, Width, Height)
+ */
+ Common::Rect &GetDisplayRect() {
+ return m_ScreenRect;
+ }
+
+ /**
+ * Returns the bit depth of the output buffer
+ */
+ int GetBitDepth() {
+ return m_BitDepth;
+ }
+
+ /**
+ * Determines whether the frame buffer change is to be synchronised with Vsync. This is turned on by default.
+ * Notes: In windowed mode, this setting has no effect.
+ * @param Vsync Indicates whether the frame buffer changes are to be synchronised with Vsync.
+ */
+ void SetVsync(bool Vsync);
+
+ /**
+ * Returns true if V-Sync is on.
+ * Notes: In windowed mode, this setting has no effect.
+ */
+ bool GetVsync() const;
+
+ /**
+ * Returns true if the engine is running in Windowed mode.
+ */
+ bool IsWindowed() {
+ return m_Windowed;
+ }
+
+ /**
+ * Fills a rectangular area of the frame buffer with a colour.
+ * Notes: It is possible to create transparent rectangles by passing a colour with an Alpha value of 255.
+ * @param FillRectPtr Pointer to a Common::Rect, which specifies the section of the frame buffer to be filled.
+ * If the rectangle falls partly off-screen, then it is automatically trimmed.
+ * If a NULL value is passed, then the entire image is to be filled.
+ * @param Color The 32-bit colour with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ bool fill(const Common::Rect *FillRectPtr = 0, uint Color = BS_RGB(0, 0, 0));
+
+ // Debugging Methods
+
+ int GetFPSCount() const {
+ return m_FPSCounter.GetFPS();
+ }
+ int GetRepaintedPixels() const {
+ return m_RepaintedPixels;
+ }
+
+ Graphics::Surface _backSurface;
+ Graphics::Surface *getSurface() { return &_backSurface; }
+
+ Graphics::Surface _frameBuffer;
+ Graphics::Surface *getFrameBuffer() { return &_frameBuffer; }
+
+ Common::MemoryReadStream *_thumbnail;
+ Common::MemoryReadStream *getThumbnail() { return _thumbnail; }
+
+ // Access methods
+
+ /**
+ * Returns the size of a pixel entry in bytes for a particular colour format
+ * @param ColorFormat The desired colour format. The parameter must be of type COLOR_FORMATS
+ * @return Returns the size of a pixel in bytes. If the colour format is unknown, -1 is returned.
+ */
+ static int GetPixelSize(GraphicEngine::COLOR_FORMATS ColorFormat) {
+ switch (ColorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ return 4;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Calculates the length of an image line in bytes, depending on a given colour format.
+ * @param ColorFormat The colour format
+ * @param Width The width of the line in pixels
+ * @return Reflects the length of the line in bytes. If the colour format is
+ * unknown, -1 is returned
+ */
+ static int CalcPitch(GraphicEngine::COLOR_FORMATS ColorFormat, int Width) {
+ switch (ColorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ return Width * 4;
+
+ default:
+ BS_ASSERT(false);
+ }
+
+ return -1;
+ }
+
+ // Resource-Managing Methods
+ // --------------------------
+ virtual Resource *loadResource(const Common::String &fileName);
+ virtual bool canLoadResource(const Common::String &fileName);
+
+ // Persistence Methods
+ // -------------------
+ virtual bool persist(OutputPersistenceBlock &Writer);
+ virtual bool unpersist(InputPersistenceBlock &Reader);
+
+ static void ARGBColorToLuaColor(lua_State *L, uint Color);
+ static uint LuaColorToARGBColor(lua_State *L, int StackIndex);
+
+protected:
+
+ // Display Variables
+ // -----------------
+ int m_Width;
+ int m_Height;
+ Common::Rect m_ScreenRect;
+ int m_BitDepth;
+ bool m_Windowed;
+
+ // Debugging Variables
+ // -------------------
+ Framecounter m_FPSCounter;
+
+ uint m_RepaintedPixels;
+
+ /**
+ * Calculates the time since the last frame beginning has passed.
+ */
+ void UpdateLastFrameDuration();
+
+private:
+ bool RegisterScriptBindings();
+
+ // LastFrameDuration Variables
+ // ---------------------------
+ uint64 m_LastTimeStamp;
+ uint m_LastFrameDuration;
+ bool m_TimerActive;
+ Common::Array<uint> m_FrameTimeSamples;
+ uint m_FrameTimeSampleSlot;
+
+private:
+ byte *_backBuffer;
+
+ RenderObjectPtr<Panel> m_MainPanelPtr;
+
+ Common::ScopedPtr<RenderObjectManager> _renderObjectManagerPtr;
+
+ struct DebugLine {
+ DebugLine(const Vertex &_Start, const Vertex &_End, uint _Color) :
+ Start(_Start),
+ End(_End),
+ Color(_Color) {}
+ DebugLine() {}
+
+ Vertex Start;
+ Vertex End;
+ uint Color;
+ };
+
+ Common::Array<DebugLine> m_DebugLines;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/graphicengine_script.cpp b/engines/sword25/gfx/graphicengine_script.cpp
new file mode 100644
index 0000000000..d443023436
--- /dev/null
+++ b/engines/sword25/gfx/graphicengine_script.cpp
@@ -0,0 +1,1553 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/callbackregistry.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+#include "sword25/script/luacallback.h"
+#include "sword25/math/vertex.h"
+
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/renderobject.h"
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/animation.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/text.h"
+#include "sword25/gfx/animationtemplate.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "GRAPHICENGINE"
+
+// -----------------------------------------------------------------------------
+// Callback-Objekte
+// -----------------------------------------------------------------------------
+
+static bool AnimationDeleteCallback(uint Data);
+static bool AnimationActionCallback(uint Data);
+static bool AnimationLoopPointCallback(uint Data);
+
+namespace {
+// -------------------------------------------------------------------------
+
+class ActionCallback : public LuaCallback {
+public:
+ ActionCallback(lua_State *L) : LuaCallback(L) {};
+
+ Common::String Action;
+
+protected:
+ virtual int PreFunctionInvokation(lua_State *L) {
+ lua_pushstring(L, Action.c_str());
+ return 1;
+ }
+};
+
+Common::ScopedPtr<LuaCallback> LoopPointCallbackPtr;
+Common::ScopedPtr<ActionCallback> ActionCallbackPtr;
+
+// -------------------------------------------------------------------------
+
+struct CallbackfunctionRegisterer {
+ CallbackfunctionRegisterer() {
+ CallbackRegistry::getInstance().registerCallbackFunction("LuaLoopPointCB", (void ( *)(int))AnimationLoopPointCallback);
+ CallbackRegistry::getInstance().registerCallbackFunction("LuaActionCB", (void ( *)(int))AnimationActionCallback);
+ CallbackRegistry::getInstance().registerCallbackFunction("LuaDeleteCB", (void ( *)(int))AnimationDeleteCallback);
+ }
+};
+static CallbackfunctionRegisterer Instance;
+}
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+// Die Strings werden als #defines definiert um Stringkomposition zur Compilezeit zu ermöglichen.
+#define RENDEROBJECT_CLASS_NAME "Gfx.RenderObject"
+#define BITMAP_CLASS_NAME "Gfx.Bitmap"
+#define PANEL_CLASS_NAME "Gfx.Panel"
+#define TEXT_CLASS_NAME "Gfx.Text"
+#define ANIMATION_CLASS_NAME "Gfx.Animation"
+#define ANIMATION_TEMPLATE_CLASS_NAME "Gfx.AnimationTemplate"
+static const char *GFX_LIBRARY_NAME = "Gfx";
+
+// -----------------------------------------------------------------------------
+
+// Wie luaL_checkudata, nur ohne dass kein Fehler erzeugt wird.
+static void *my_checkudata(lua_State *L, int ud, const char *tname) {
+ int top = lua_gettop(L);
+
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ // lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ LuaBindhelper::getMetatable(L, tname);
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_settop(L, top);
+ return p;
+ }
+ }
+ }
+
+ lua_settop(L, top);
+ return NULL;
+}
+
+// -----------------------------------------------------------------------------
+
+static void NewUintUserData(lua_State *L, uint Value) {
+ void *UserData = lua_newuserdata(L, sizeof(Value));
+ memcpy(UserData, &Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+static AnimationTemplate *CheckAnimationTemplate(lua_State *L, int idx = 1) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.AnimationTemplate
+ uint AnimationTemplateHandle;
+ if ((AnimationTemplateHandle = *reinterpret_cast<uint *>(my_checkudata(L, idx, ANIMATION_TEMPLATE_CLASS_NAME))) != 0) {
+ AnimationTemplate *AnimationTemplatePtr = AnimationTemplateRegistry::getInstance().resolveHandle(AnimationTemplateHandle);
+ if (!AnimationTemplatePtr)
+ luaL_error(L, "The animation template with the handle %d does no longer exist.", AnimationTemplateHandle);
+ return AnimationTemplatePtr;
+ } else {
+ luaL_argcheck(L, 0, idx, "'" ANIMATION_TEMPLATE_CLASS_NAME "' expected");
+ return 0;
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+static int NewAnimationTemplate(lua_State *L) {
+ uint AnimationTemplateHandle = AnimationTemplate::create(luaL_checkstring(L, 1));
+ AnimationTemplate *AnimationTemplatePtr = AnimationTemplateRegistry::getInstance().resolveHandle(AnimationTemplateHandle);
+ if (AnimationTemplatePtr && AnimationTemplatePtr->isValid()) {
+ NewUintUserData(L, AnimationTemplateHandle);
+ //luaL_getmetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, ANIMATION_TEMPLATE_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_AddFrame(lua_State *L) {
+ AnimationTemplate *pAT = CheckAnimationTemplate(L);
+ pAT->addFrame(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetFrame(lua_State *L) {
+ AnimationTemplate *pAT = CheckAnimationTemplate(L);
+ pAT->setFrame(static_cast<int>(luaL_checknumber(L, 2)), static_cast<int>(luaL_checknumber(L, 3)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationTypeStringToNumber(const char *TypeString, Animation::ANIMATION_TYPES &Result) {
+ if (strcmp(TypeString, "jojo") == 0) {
+ Result = Animation::AT_JOJO;
+ return true;
+ } else if (strcmp(TypeString, "loop") == 0) {
+ Result = Animation::AT_LOOP;
+ return true;
+ } else if (strcmp(TypeString, "oneshot") == 0) {
+ Result = Animation::AT_ONESHOT;
+ return true;
+ } else
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetAnimationType(lua_State *L) {
+ AnimationTemplate *pAT = CheckAnimationTemplate(L);
+ Animation::ANIMATION_TYPES AnimationType;
+ if (AnimationTypeStringToNumber(luaL_checkstring(L, 2), AnimationType)) {
+ pAT->setAnimationType(AnimationType);
+ } else {
+ luaL_argcheck(L, 0, 2, "Invalid animation type");
+ }
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_SetFPS(lua_State *L) {
+ AnimationTemplate *pAT = CheckAnimationTemplate(L);
+ pAT->setFPS(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int AT_Finalize(lua_State *L) {
+ AnimationTemplate *pAT = CheckAnimationTemplate(L);
+ delete pAT;
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg ANIMATION_TEMPLATE_METHODS[] = {
+ {"AddFrame", AT_AddFrame},
+ {"SetFrame", AT_SetFrame},
+ {"SetAnimationType", AT_SetAnimationType},
+ {"SetFPS", AT_SetFPS},
+ {"__gc", AT_Finalize},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static GraphicEngine *GetGE() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ GraphicEngine *pGE = static_cast<GraphicEngine *>(pKernel->GetService("gfx"));
+ BS_ASSERT(pGE);
+ return pGE;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Init(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ switch (lua_gettop(L)) {
+ case 0:
+ lua_pushbooleancpp(L, pGE->Init());
+ break;
+ case 1:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1))));
+ break;
+ case 2:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2))));
+ break;
+ case 3:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3))));
+ break;
+ case 4:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4))));
+ break;
+ default:
+ lua_pushbooleancpp(L, pGE->Init(static_cast<int>(luaL_checknumber(L, 1)), static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)), static_cast<int>(luaL_checknumber(L, 4)),
+ lua_tobooleancpp(L, 5)));
+ }
+
+
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Main-Panel zum Gfx-Modul hinzufügen
+ RenderObjectPtr<Panel> MainPanelPtr(GetGE()->GetMainPanel());
+ BS_ASSERT(MainPanelPtr.isValid());
+
+ lua_pushstring(L, GFX_LIBRARY_NAME);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ BS_ASSERT(!lua_isnil(L, -1));
+
+ NewUintUserData(L, MainPanelPtr->getHandle());
+ BS_ASSERT(!lua_isnil(L, -1));
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ lua_pushstring(L, "MainPanel");
+ lua_insert(L, -2);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StartFrame(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ if (lua_gettop(L) == 0)
+ lua_pushbooleancpp(L, pGE->StartFrame());
+ else
+ lua_pushbooleancpp(L, pGE->StartFrame(lua_tobooleancpp(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int EndFrame(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->EndFrame());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int DrawDebugLine(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ Vertex Start;
+ Vertex End;
+ Vertex::luaVertexToVertex(L, 1, Start);
+ Vertex::luaVertexToVertex(L, 2, End);
+ pGE->DrawDebugLine(Start, End, GraphicEngine::LuaColorToARGBColor(L, 3));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetDisplayWidth(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetDisplayWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetDisplayHeight(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetDisplayHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetBitDepth(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetBitDepth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetVsync(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ pGE->SetVsync(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsVsync(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->GetVsync());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsWindowed(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushbooleancpp(L, pGE->IsWindowed());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetFPSCount(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetFPSCount());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetLastFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetLastFrameDuration());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StopMainTimer(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+ pGE->StopMainTimer();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ResumeMainTimer(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+ pGE->ResumeMainTimer();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSecondaryFrameDuration(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+
+ lua_pushnumber(L, pGE->GetSecondaryFrameDuration());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveScreenshot(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+ lua_pushbooleancpp(L, pGE->SaveScreenshot(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveThumbnailScreenshot(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+ lua_pushbooleancpp(L, pGE->SaveThumbnailScreenshot(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetRepaintedPixels(lua_State *L) {
+ GraphicEngine *pGE = GetGE();
+ lua_pushnumber(L, static_cast<lua_Number>(pGE->GetRepaintedPixels()));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg GFX_FUNCTIONS[] = {
+ {"Init", Init},
+ {"StartFrame", StartFrame},
+ {"EndFrame", EndFrame},
+ {"DrawDebugLine", DrawDebugLine},
+ {"SetVsync", SetVsync},
+ {"GetDisplayWidth", GetDisplayWidth},
+ {"GetDisplayHeight", GetDisplayHeight},
+ {"GetBitDepth", GetBitDepth},
+ {"IsVsync", IsVsync},
+ {"IsWindowed", IsWindowed},
+ {"GetFPSCount", GetFPSCount},
+ {"GetLastFrameDuration", GetLastFrameDuration},
+ {"StopMainTimer", StopMainTimer},
+ {"ResumeMainTimer", ResumeMainTimer},
+ {"GetSecondaryFrameDuration", GetSecondaryFrameDuration},
+ {"SaveScreenshot", SaveScreenshot},
+ {"NewAnimationTemplate", NewAnimationTemplate},
+ {"GetRepaintedPixels", GetRepaintedPixels},
+ {"SaveThumbnailScreenshot", SaveThumbnailScreenshot},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static RenderObjectPtr<RenderObject> CheckRenderObject(lua_State *L, bool ErrorIfRemoved = true) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable einer Klasse haben, die von Gfx.RenderObject "erbt".
+ uint *UserDataPtr;
+ if ((UserDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0 ||
+ (UserDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> ROPtr(* UserDataPtr);
+ if (ROPtr.isValid())
+ return ROPtr;
+ else {
+ if (ErrorIfRemoved)
+ luaL_error(L, "The renderobject with the handle %d does no longer exist.", * UserDataPtr);
+ }
+ } else {
+ luaL_argcheck(L, 0, 1, "'" RENDEROBJECT_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<RenderObject>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetPos(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ Vertex Pos;
+ Vertex::luaVertexToVertex(L, 2, Pos);
+ ROPtr->setPos(Pos.x, Pos.y);
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetX(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr->setX(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetY(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr->setY(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr->setZ(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_SetVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr->setVisible(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetX(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetY(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetZ(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getZ());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetAbsoluteX(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getAbsoluteX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetAbsoluteY(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getAbsoluteY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetWidth(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_GetHeight(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushnumber(L, ROPtr->getHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_IsVisible(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ lua_pushbooleancpp(L, ROPtr->isVisible());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddPanel(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ RenderObjectPtr<Panel> PanelPtr = ROPtr->addPanel(static_cast<int>(luaL_checknumber(L, 2)),
+ static_cast<int>(luaL_checknumber(L, 3)),
+ GraphicEngine::LuaColorToARGBColor(L, 4));
+ if (PanelPtr.isValid()) {
+ NewUintUserData(L, PanelPtr->getHandle());
+ // luaL_getmetatable(L, PANEL_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, PANEL_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddBitmap(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ RenderObjectPtr<Bitmap> BitmaPtr = ROPtr->addBitmap(luaL_checkstring(L, 2));
+ if (BitmaPtr.isValid()) {
+ NewUintUserData(L, BitmaPtr->getHandle());
+ // luaL_getmetatable(L, BITMAP_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, BITMAP_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddText(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+
+ RenderObjectPtr<Text> TextPtr;
+ if (lua_gettop(L) >= 3) TextPtr = ROPtr->addText(luaL_checkstring(L, 2), luaL_checkstring(L, 3));
+ else TextPtr = ROPtr->addText(luaL_checkstring(L, 2));
+
+ if (TextPtr.isValid()) {
+ NewUintUserData(L, TextPtr->getHandle());
+ // luaL_getmetatable(L, TEXT_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, TEXT_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int RO_AddAnimation(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+
+ RenderObjectPtr<Animation> AnimationPtr;
+ if (lua_type(L, 2) == LUA_TUSERDATA)
+ AnimationPtr = ROPtr->addAnimation(*CheckAnimationTemplate(L, 2));
+ else
+ AnimationPtr = ROPtr->addAnimation(luaL_checkstring(L, 2));
+
+ if (AnimationPtr.isValid()) {
+ NewUintUserData(L, AnimationPtr->getHandle());
+ // luaL_getmetatable(L, ANIMATION_CLASS_NAME);
+ LuaBindhelper::getMetatable(L, ANIMATION_CLASS_NAME);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ // Alle Animationscallbacks registrieren.
+ AnimationPtr->registerDeleteCallback(AnimationDeleteCallback, AnimationPtr->getHandle());
+ AnimationPtr->registerLoopPointCallback(AnimationLoopPointCallback, AnimationPtr->getHandle());
+ AnimationPtr->registerActionCallback(AnimationActionCallback, AnimationPtr->getHandle());
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg RENDEROBJECT_METHODS[] = {
+ {"AddAnimation", RO_AddAnimation},
+ {"AddText", RO_AddText},
+ {"AddBitmap", RO_AddBitmap},
+ {"AddPanel", RO_AddPanel},
+ {"SetPos", RO_SetPos},
+ {"SetX", RO_SetX},
+ {"SetY", RO_SetY},
+ {"SetZ", RO_SetZ},
+ {"SetVisible", RO_SetVisible},
+ {"GetX", RO_GetX},
+ {"GetY", RO_GetY},
+ {"GetZ", RO_GetZ},
+ {"GetAbsoluteX", RO_GetAbsoluteX},
+ {"GetAbsoluteY", RO_GetAbsoluteY},
+ {"GetWidth", RO_GetWidth},
+ {"GetHeight", RO_GetHeight},
+ {"IsVisible", RO_IsVisible},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static RenderObjectPtr<Panel> CheckPanel(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Panel
+ uint *UserDataPtr;
+ if ((UserDataPtr = (uint *) my_checkudata(L, 1, PANEL_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.isValid()) {
+ return ROPtr->toPanel();
+ } else
+ luaL_error(L, "The panel with the handle %d does no longer exist.", *UserDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" PANEL_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Panel>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_GetColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = CheckPanel(L);
+ BS_ASSERT(PanelPtr.isValid());
+ GraphicEngine::ARGBColorToLuaColor(L, PanelPtr->getColor());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_SetColor(lua_State *L) {
+ RenderObjectPtr<Panel> PanelPtr = CheckPanel(L);
+ BS_ASSERT(PanelPtr.isValid());
+ PanelPtr->setColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int P_Remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr.erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg PANEL_METHODS[] = {
+ {"GetColor", P_GetColor},
+ {"SetColor", P_SetColor},
+ {"Remove", P_Remove},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static RenderObjectPtr<Bitmap> CheckBitmap(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Bitmap
+ uint *UserDataPtr;
+ if ((UserDataPtr = (uint *) my_checkudata(L, 1, BITMAP_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.isValid()) {
+ return ROPtr->toBitmap();
+ } else
+ luaL_error(L, "The bitmap with the handle %d does no longer exist.", *UserDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" BITMAP_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Bitmap>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setAlpha(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setModulationColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactor(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setFlipH(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_SetFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ BitmapPtr->setFlipV(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetAlpha(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushnumber(L, BitmapPtr->getAlpha());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetTintColor(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->getModulationColor());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushnumber(L, BitmapPtr->getScaleFactorX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushnumber(L, BitmapPtr->getScaleFactorY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsFlipH(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushbooleancpp(L, BitmapPtr->isFlipH());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsFlipV(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushbooleancpp(L, BitmapPtr->isFlipV());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_GetPixel(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ Vertex Pos;
+ Vertex::luaVertexToVertex(L, 2, Pos);
+ GraphicEngine::ARGBColorToLuaColor(L, BitmapPtr->getPixel(Pos.x, Pos.y));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushbooleancpp(L, BitmapPtr->isScalingAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushbooleancpp(L, BitmapPtr->isAlphaAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int B_IsTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Bitmap> BitmapPtr = CheckBitmap(L);
+ BS_ASSERT(BitmapPtr.isValid());
+ lua_pushbooleancpp(L, BitmapPtr->isColorModulationAllowed());
+ return 1;
+}
+// -----------------------------------------------------------------------------
+
+static int B_Remove(lua_State *L) {
+ RenderObjectPtr<RenderObject> ROPtr = CheckRenderObject(L);
+ BS_ASSERT(ROPtr.isValid());
+ ROPtr.erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg BITMAP_METHODS[] = {
+ {"SetAlpha", B_SetAlpha},
+ {"SetTintColor", B_SetTintColor},
+ {"SetScaleFactor", B_SetScaleFactor},
+ {"SetScaleFactorX", B_SetScaleFactorX},
+ {"SetScaleFactorY", B_SetScaleFactorY},
+ {"SetFlipH", B_SetFlipH},
+ {"SetFlipV", B_SetFlipV},
+ {"GetAlpha", B_GetAlpha},
+ {"GetTintColor", B_GetTintColor},
+ {"GetScaleFactorX", B_GetScaleFactorX},
+ {"GetScaleFactorY", B_GetScaleFactorY},
+ {"IsFlipH", B_IsFlipH},
+ {"IsFlipV", B_IsFlipV},
+ {"GetPixel", B_GetPixel},
+ {"IsScalingAllowed", B_IsScalingAllowed},
+ {"IsAlphaAllowed", B_IsAlphaAllowed},
+ {"IsTintingAllowed", B_IsTintingAllowed},
+ {"Remove", B_Remove},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static RenderObjectPtr<Animation> CheckAnimation(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Animation
+ uint *UserDataPtr;
+ if ((UserDataPtr = (uint *) my_checkudata(L, 1, ANIMATION_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.isValid())
+ return ROPtr->toAnimation();
+ else {
+ luaL_error(L, "The animation with the handle %d does no longer exist.", *UserDataPtr);
+ }
+ } else {
+ luaL_argcheck(L, 0, 1, "'" ANIMATION_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Animation>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Play(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->play();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Pause(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->pause();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Stop(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->stop();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetFrame(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setFrame(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetAlpha(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+// -----------------------------------------------------------------------------
+
+static int A_SetTintColor(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setModulationColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactor(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setScaleFactor(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setScaleFactorX(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_SetScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr->setScaleFactorY(static_cast<float>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetScaleFactorX(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushnumber(L, AnimationPtr->getScaleFactorX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetScaleFactorY(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushnumber(L, AnimationPtr->getScaleFactorY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetAnimationType(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ switch (AnimationPtr->getAnimationType()) {
+ case Animation::AT_JOJO:
+ lua_pushstring(L, "jojo");
+ break;
+ case Animation::AT_LOOP:
+ lua_pushstring(L, "loop");
+ break;
+ case Animation::AT_ONESHOT:
+ lua_pushstring(L, "oneshot");
+ break;
+ default:
+ BS_ASSERT(false);
+ }
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetFPS(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushnumber(L, AnimationPtr->getFPS());
+ return 1;
+}
+
+
+// -----------------------------------------------------------------------------
+
+static int A_GetFrameCount(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushnumber(L, AnimationPtr->getFrameCount());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsScalingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushbooleancpp(L, AnimationPtr->isScalingAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsAlphaAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushbooleancpp(L, AnimationPtr->isAlphaAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsTintingAllowed(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushbooleancpp(L, AnimationPtr->isColorModulationAllowed());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetCurrentFrame(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushnumber(L, AnimationPtr->getCurrentFrame());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_GetCurrentAction(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushstring(L, AnimationPtr->getCurrentAction().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_IsPlaying(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ lua_pushbooleancpp(L, AnimationPtr->isRunning());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationLoopPointCallback(uint Handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
+ LoopPointCallbackPtr->invokeCallbackFunctions(L, Handle);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_RegisterLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ LoopPointCallbackPtr->registerCallbackFunction(L, AnimationPtr->getHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_UnregisterLoopPointCallback(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ LoopPointCallbackPtr->unregisterCallbackFunction(L, AnimationPtr->getHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationActionCallback(uint Handle) {
+ RenderObjectPtr<Animation> AnimationPtr(Handle);
+ if (AnimationPtr.isValid()) {
+ ActionCallbackPtr->Action = AnimationPtr->getCurrentAction();
+ lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
+ ActionCallbackPtr->invokeCallbackFunctions(L, AnimationPtr->getHandle());
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_RegisterActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ ActionCallbackPtr->registerCallbackFunction(L, AnimationPtr->getHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_UnregisterActionCallback(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ ActionCallbackPtr->unregisterCallbackFunction(L, AnimationPtr->getHandle());
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static bool AnimationDeleteCallback(uint Handle) {
+ lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
+ LoopPointCallbackPtr->removeAllObjectCallbacks(L, Handle);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+static int A_Remove(lua_State *L) {
+ RenderObjectPtr<Animation> AnimationPtr = CheckAnimation(L);
+ BS_ASSERT(AnimationPtr.isValid());
+ AnimationPtr.erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg ANIMATION_METHODS[] = {
+ {"Play", A_Play},
+ {"Pause", A_Pause},
+ {"Stop", A_Stop},
+ {"SetFrame", A_SetFrame},
+ {"SetAlpha", A_SetAlpha},
+ {"SetTintColor", A_SetTintColor},
+ {"SetScaleFactor", A_SetScaleFactor},
+ {"SetScaleFactorX", A_SetScaleFactorX},
+ {"SetScaleFactorY", A_SetScaleFactorY},
+ {"GetScaleFactorX", A_GetScaleFactorX},
+ {"GetScaleFactorY", A_GetScaleFactorY},
+ {"GetAnimationType", A_GetAnimationType},
+ {"GetFPS", A_GetFPS},
+ {"GetFrameCount", A_GetFrameCount},
+ {"IsScalingAllowed", A_IsScalingAllowed},
+ {"IsAlphaAllowed", A_IsAlphaAllowed},
+ {"IsTintingAllowed", A_IsTintingAllowed},
+ {"GetCurrentFrame", A_GetCurrentFrame},
+ {"GetCurrentAction", A_GetCurrentAction},
+ {"IsPlaying", A_IsPlaying},
+ {"RegisterLoopPointCallback", A_RegisterLoopPointCallback},
+ {"UnregisterLoopPointCallback", A_UnregisterLoopPointCallback},
+ {"RegisterActionCallback", A_RegisterActionCallback},
+ {"UnregisterActionCallback", A_UnregisterActionCallback},
+ {"Remove", A_Remove},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static RenderObjectPtr<Text> CheckText(lua_State *L) {
+ // Der erste Parameter muss vom Typ userdata sein und die Metatable der Klasse Gfx.Text
+ uint *UserDataPtr;
+ if ((UserDataPtr = (uint *) my_checkudata(L, 1, TEXT_CLASS_NAME)) != 0) {
+ RenderObjectPtr<RenderObject> ROPtr(*UserDataPtr);
+ if (ROPtr.isValid())
+ return ROPtr->toText();
+ else
+ luaL_error(L, "The text with the handle %d does no longer exist.", *UserDataPtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" TEXT_CLASS_NAME "' expected");
+ }
+
+ return RenderObjectPtr<Text>();
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetFont(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->SetFont(luaL_checkstring(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetText(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->SetText(luaL_checkstring(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAlpha(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->setAlpha(static_cast<int>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetColor(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->setColor(GraphicEngine::LuaColorToARGBColor(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->SetAutoWrap(lua_tobooleancpp(L, 2));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_SetAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr->SetAutoWrapThreshold(static_cast<uint>(luaL_checknumber(L, 2)));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetText(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushstring(L, TextPtr->GetText().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetFont(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushstring(L, TextPtr->GetFont().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetAlpha(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushnumber(L, TextPtr->getAlpha());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetColor(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushnumber(L, TextPtr->getColor());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_IsAutoWrap(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushbooleancpp(L, TextPtr->IsAutoWrapActive());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_GetAutoWrapThreshold(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ lua_pushnumber(L, TextPtr->GetAutoWrapThreshold());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int T_Remove(lua_State *L) {
+ RenderObjectPtr<Text> TextPtr = CheckText(L);
+ BS_ASSERT(TextPtr.isValid());
+ TextPtr.erase();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const luaL_reg TEXT_METHODS[] = {
+ {"SetFont", T_SetFont},
+ {"SetText", T_SetText},
+ {"SetAlpha", T_SetAlpha},
+ {"SetColor", T_SetColor},
+ {"SetAutoWrap", T_SetAutoWrap},
+ {"SetAutoWrapThreshold", T_SetAutoWrapThreshold},
+ {"GetText", T_GetText},
+ {"GetFont", T_GetFont},
+ {"GetAlpha", T_GetAlpha},
+ {"GetColor", T_GetColor},
+ {"IsAutoWrap", T_IsAutoWrap},
+ {"GetAutoWrapThreshold", T_GetAutoWrapThreshold},
+ {"Remove", T_Remove},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+bool GraphicEngine::RegisterScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addMethodsToClass(L, BITMAP_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, PANEL_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, TEXT_CLASS_NAME, RENDEROBJECT_METHODS)) return false;
+
+ if (!LuaBindhelper::addMethodsToClass(L, PANEL_CLASS_NAME, PANEL_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, BITMAP_CLASS_NAME, BITMAP_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, TEXT_CLASS_NAME, TEXT_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_CLASS_NAME, ANIMATION_METHODS)) return false;
+
+ if (!LuaBindhelper::addMethodsToClass(L, ANIMATION_TEMPLATE_CLASS_NAME, ANIMATION_TEMPLATE_METHODS)) return false;
+
+ if (!LuaBindhelper::addFunctionsToLib(L, GFX_LIBRARY_NAME, GFX_FUNCTIONS)) return false;
+
+ LoopPointCallbackPtr.reset(new LuaCallback(L));
+ ActionCallbackPtr.reset(new ActionCallback(L));
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/art.cpp b/engines/sword25/gfx/image/art.cpp
new file mode 100644
index 0000000000..e9aacbcf24
--- /dev/null
+++ b/engines/sword25/gfx/image/art.cpp
@@ -0,0 +1,2651 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/* Various utility functions RLL finds useful. */
+
+#include "sword25/gfx/image/art.h"
+
+namespace Sword25 {
+
+/**
+ * art_die: Print the error message to stderr and exit with a return code of 1.
+ * @fmt: The printf-style format for the error message.
+ *
+ * Used for dealing with severe errors.
+ **/
+void art_die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/**
+ * art_warn: Print the warning message to stderr.
+ * @fmt: The printf-style format for the warning message.
+ *
+ * Used for generating warnings.
+ **/
+void art_warn(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * art_svp_free: Free an #ArtSVP structure.
+ * @svp: #ArtSVP to free.
+ *
+ * Frees an #ArtSVP structure and all the segments in it.
+ **/
+void art_svp_free(ArtSVP *svp) {
+ int n_segs = svp->n_segs;
+ int i;
+
+ for (i = 0; i < n_segs; i++)
+ free(svp->segs[i].points);
+ free(svp);
+}
+
+#define EPSILON 0
+
+/**
+ * art_svp_seg_compare: Compare two segments of an svp.
+ * @seg1: First segment to compare.
+ * @seg2: Second segment to compare.
+ *
+ * Compares two segments of an svp. Return 1 if @seg2 is below or to the
+ * right of @seg1, -1 otherwise.
+ **/
+int art_svp_seg_compare(const void *s1, const void *s2) {
+ const ArtSVPSeg *seg1 = (const ArtSVPSeg *)s1;
+ const ArtSVPSeg *seg2 = (const ArtSVPSeg *)s2;
+
+ if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
+ else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
+ else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
+ else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
+ else if ((seg1->points[1].x - seg1->points[0].x) *
+ (seg2->points[1].y - seg2->points[0].y) -
+ (seg1->points[1].y - seg1->points[0].y) *
+ (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
+ else return -1;
+}
+
+/**
+ * art_vpath_add_point: Add point to vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @code: The pathcode for the new point.
+ * @x: The X coordinate of the new point.
+ * @y: The Y coordinate of the new point.
+ *
+ * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath
+ * and *@pn_points_max as necessary. *@pn_points is incremented.
+ *
+ * This routine always adds the point after all points already in the
+ * vpath. Thus, it should be called in the order the points are
+ * desired.
+ **/
+void art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y) {
+ int i;
+
+ i = (*pn_points)++;
+ if (i == *pn_points_max)
+ art_expand(*p_vpath, ArtVpath, *pn_points_max);
+ (*p_vpath)[i].code = code;
+ (*p_vpath)[i].x = x;
+ (*p_vpath)[i].y = y;
+}
+
+/* Sort vector paths into sorted vector paths */
+
+/* reverse a list of points in place */
+static void reverse_points(ArtPoint *points, int n_points) {
+ int i;
+ ArtPoint tmp_p;
+
+ for (i = 0; i < (n_points >> 1); i++) {
+ tmp_p = points[i];
+ points[i] = points[n_points - (i + 1)];
+ points[n_points - (i + 1)] = tmp_p;
+ }
+}
+
+/**
+ * art_svp_from_vpath: Convert a vpath to a sorted vector path.
+ * @vpath: #ArtVPath to convert.
+ *
+ * Converts a vector path into sorted vector path form. The svp form is
+ * more efficient for rendering and other vector operations.
+ *
+ * Basically, the implementation is to traverse the vector path,
+ * generating a new segment for each "run" of points in the vector
+ * path with monotonically increasing Y values. All the resulting
+ * values are then sorted.
+ *
+ * Note: I'm not sure that the sorting rule is correct with respect
+ * to numerical stability issues.
+ *
+ * Return value: Resulting sorted vector path.
+ **/
+ArtSVP *art_svp_from_vpath(ArtVpath *vpath) {
+ int n_segs, n_segs_max;
+ ArtSVP *svp;
+ int dir;
+ int new_dir;
+ int i;
+ ArtPoint *points;
+ int n_points, n_points_max;
+ double x, y;
+ double x_min, x_max;
+
+ n_segs = 0;
+ n_segs_max = 16;
+ svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+ (n_segs_max - 1) * sizeof(ArtSVPSeg));
+
+ dir = 0;
+ n_points = 0;
+ n_points_max = 0;
+ points = NULL;
+ i = 0;
+
+ x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
+ but it makes gcc -Wall -ansi -pedantic happier */
+ x_min = x_max = 0; /* same */
+
+ while (vpath[i].code != ART_END) {
+ if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) {
+ if (points != NULL && n_points >= 2) {
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ points = NULL;
+ }
+
+ if (points == NULL) {
+ n_points_max = 4;
+ points = art_new(ArtPoint, n_points_max);
+ }
+
+ n_points = 1;
+ points[0].x = x = vpath[i].x;
+ points[0].y = y = vpath[i].y;
+ x_min = x;
+ x_max = x;
+ dir = 0;
+ } else { /* must be LINETO */
+ new_dir = (vpath[i].y > y ||
+ (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
+ if (dir && dir != new_dir) {
+ /* new segment */
+ x = points[n_points - 1].x;
+ y = points[n_points - 1].y;
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+
+ n_points = 1;
+ n_points_max = 4;
+ points = art_new(ArtPoint, n_points_max);
+ points[0].x = x;
+ points[0].y = y;
+ x_min = x;
+ x_max = x;
+ }
+
+ if (points != NULL) {
+ if (n_points == n_points_max)
+ art_expand(points, ArtPoint, n_points_max);
+ points[n_points].x = x = vpath[i].x;
+ points[n_points].y = y = vpath[i].y;
+ if (x < x_min) x_min = x;
+ else if (x > x_max) x_max = x;
+ n_points++;
+ }
+ dir = new_dir;
+ }
+ i++;
+ }
+
+ if (points != NULL) {
+ if (n_points >= 2) {
+ if (n_segs == n_segs_max) {
+ n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ }
+ svp->segs[n_segs].n_points = n_points;
+ svp->segs[n_segs].dir = (dir > 0);
+ if (dir < 0)
+ reverse_points(points, n_points);
+ svp->segs[n_segs].points = points;
+ svp->segs[n_segs].bbox.x0 = x_min;
+ svp->segs[n_segs].bbox.x1 = x_max;
+ svp->segs[n_segs].bbox.y0 = points[0].y;
+ svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+ n_segs++;
+ } else
+ free(points);
+ }
+
+ svp->n_segs = n_segs;
+
+ qsort(&svp->segs, n_segs, sizeof(ArtSVPSeg), art_svp_seg_compare);
+
+ return svp;
+}
+
+
+/* Basic constructors and operations for bezier paths */
+
+#define RENDER_LEVEL 4
+#define RENDER_SIZE (1 << (RENDER_LEVEL))
+
+/**
+ * art_vpath_render_bez: Render a bezier segment into the vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @x0: X coordinate of starting bezier point.
+ * @y0: Y coordinate of starting bezier point.
+ * @x1: X coordinate of first bezier control point.
+ * @y1: Y coordinate of first bezier control point.
+ * @x2: X coordinate of second bezier control point.
+ * @y2: Y coordinate of second bezier control point.
+ * @x3: X coordinate of ending bezier point.
+ * @y3: Y coordinate of ending bezier point.
+ * @flatness: Flatness control.
+ *
+ * Renders a bezier segment into the vector path, reallocating and
+ * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is
+ * incremented by the number of vector points added.
+ *
+ * This step includes (@x0, @y0) but not (@x3, @y3).
+ *
+ * The @flatness argument guides the amount of subdivision. The Adobe
+ * PostScript reference manual defines flatness as the maximum
+ * deviation between the any point on the vpath approximation and the
+ * corresponding point on the "true" curve, and we follow this
+ * definition here. A value of 0.25 should ensure high quality for aa
+ * rendering.
+**/
+static void art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max,
+ double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ double flatness) {
+ double x3_0, y3_0;
+ double z3_0_dot;
+ double z1_dot, z2_dot;
+ double z1_perp, z2_perp;
+ double max_perp_sq;
+
+ double x_m, y_m;
+ double xa1, ya1;
+ double xa2, ya2;
+ double xb1, yb1;
+ double xb2, yb2;
+
+ /* It's possible to optimize this routine a fair amount.
+
+ First, once the _dot conditions are met, they will also be met in
+ all further subdivisions. So we might recurse to a different
+ routine that only checks the _perp conditions.
+
+ Second, the distance _should_ decrease according to fairly
+ predictable rules (a factor of 4 with each subdivision). So it might
+ be possible to note that the distance is within a factor of 4 of
+ acceptable, and subdivide once. But proving this might be hard.
+
+ Third, at the last subdivision, x_m and y_m can be computed more
+ expeditiously (as in the routine above).
+
+ Finally, if we were able to subdivide by, say 2 or 3, this would
+ allow considerably finer-grain control, i.e. fewer points for the
+ same flatness tolerance. This would speed things up downstream.
+
+ In any case, this routine is unlikely to be the bottleneck. It's
+ just that I have this undying quest for more speed...
+
+ */
+
+ x3_0 = x3 - x0;
+ y3_0 = y3 - y0;
+
+ /* z3_0_dot is dist z0-z3 squared */
+ z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
+
+ if (z3_0_dot < 0.001) {
+ /* if start and end point are almost identical, the flatness tests
+ * don't work properly, so fall back on testing whether both of
+ * the other two control points are the same as the start point,
+ * too.
+ */
+ if (hypot(x1 - x0, y1 - y0) < 0.001
+ && hypot(x2 - x0, y2 - y0) < 0.001)
+ goto nosubdivide;
+ else
+ goto subdivide;
+ }
+
+ /* we can avoid subdivision if:
+
+ z1 has distance no more than flatness from the z0-z3 line
+
+ z1 is no more z0'ward than flatness past z0-z3
+
+ z1 is more z0'ward than z3'ward on the line traversing z0-z3
+
+ and correspondingly for z2 */
+
+ /* perp is distance from line, multiplied by dist z0-z3 */
+ max_perp_sq = flatness * flatness * z3_0_dot;
+
+ z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
+ if (z1_perp * z1_perp > max_perp_sq)
+ goto subdivide;
+
+ z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
+ if (z2_perp * z2_perp > max_perp_sq)
+ goto subdivide;
+
+ z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
+ if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq)
+ goto subdivide;
+
+ z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
+ if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
+ goto subdivide;
+
+ if (z1_dot + z1_dot > z3_0_dot)
+ goto subdivide;
+
+ if (z2_dot + z2_dot > z3_0_dot)
+ goto subdivide;
+
+
+nosubdivide:
+ /* don't subdivide */
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, x3, y3);
+ return;
+
+subdivide:
+
+ xa1 = (x0 + x1) * 0.5;
+ ya1 = (y0 + y1) * 0.5;
+ xa2 = (x0 + 2 * x1 + x2) * 0.25;
+ ya2 = (y0 + 2 * y1 + y2) * 0.25;
+ xb1 = (x1 + 2 * x2 + x3) * 0.25;
+ yb1 = (y1 + 2 * y2 + y3) * 0.25;
+ xb2 = (x2 + x3) * 0.5;
+ yb2 = (y2 + y3) * 0.5;
+ x_m = (xa2 + xb1) * 0.5;
+ y_m = (ya2 + yb1) * 0.5;
+
+ art_vpath_render_bez(p_vpath, pn, pn_max,
+ x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
+ art_vpath_render_bez(p_vpath, pn, pn_max,
+ x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
+}
+
+/**
+ * art_bez_path_to_vec: Create vpath from bezier path.
+ * @bez: Bezier path.
+ * @flatness: Flatness control.
+ *
+ * Creates a vector path closely approximating the bezier path defined by
+ * @bez. The @flatness argument controls the amount of subdivision. In
+ * general, the resulting vpath deviates by at most @flatness pixels
+ * from the "ideal" path described by @bez.
+ *
+ * Return value: Newly allocated vpath.
+ **/
+ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness) {
+ ArtVpath *vec;
+ int vec_n, vec_n_max;
+ int bez_index;
+ double x, y;
+
+ vec_n = 0;
+ vec_n_max = RENDER_SIZE;
+ vec = art_new(ArtVpath, vec_n_max);
+
+ /* Initialization is unnecessary because of the precondition that the
+ bezier path does not begin with LINETO or CURVETO, but is here
+ to make the code warning-free. */
+ x = 0;
+ y = 0;
+
+ bez_index = 0;
+ do {
+ /* make sure space for at least one more code */
+ if (vec_n >= vec_n_max)
+ art_expand(vec, ArtVpath, vec_n_max);
+ switch (bez[bez_index].code) {
+ case ART_MOVETO_OPEN:
+ case ART_MOVETO:
+ case ART_LINETO:
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = x;
+ vec[vec_n].y = y;
+ vec_n++;
+ break;
+ case ART_END:
+ vec[vec_n].code = bez[bez_index].code;
+ vec[vec_n].x = 0;
+ vec[vec_n].y = 0;
+ vec_n++;
+ break;
+ case ART_CURVETO:
+ art_vpath_render_bez(&vec, &vec_n, &vec_n_max,
+ x, y,
+ bez[bez_index].x1, bez[bez_index].y1,
+ bez[bez_index].x2, bez[bez_index].y2,
+ bez[bez_index].x3, bez[bez_index].y3,
+ flatness);
+ x = bez[bez_index].x3;
+ y = bez[bez_index].y3;
+ break;
+ }
+ } while (bez[bez_index++].code != ART_END);
+ return vec;
+}
+
+
+#define EPSILON_6 1e-6
+#define EPSILON_2 1e-12
+
+/* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
+ yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
+ y0^2 and x1^2 + y1^2 should be equal to radius^2.
+
+ A positive value of radius means curve to the left, negative means
+ curve to the right.
+*/
+static void art_svp_vpath_stroke_arc(ArtVpath **p_vpath, int *pn, int *pn_max,
+ double xc, double yc,
+ double x0, double y0,
+ double x1, double y1,
+ double radius,
+ double flatness) {
+ double theta;
+ double th_0, th_1;
+ int n_pts;
+ int i;
+ double aradius;
+
+ aradius = fabs(radius);
+ theta = 2 * M_SQRT2 * sqrt(flatness / aradius);
+ th_0 = atan2(y0, x0);
+ th_1 = atan2(y1, x1);
+ if (radius > 0) {
+ /* curve to the left */
+ if (th_0 < th_1) th_0 += M_PI * 2;
+ n_pts = ceil((th_0 - th_1) / theta);
+ } else {
+ /* curve to the right */
+ if (th_1 < th_0) th_1 += M_PI * 2;
+ n_pts = ceil((th_1 - th_0) / theta);
+ }
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + x0, yc + y0);
+ for (i = 1; i < n_pts; i++) {
+ theta = th_0 + (th_1 - th_0) * i / n_pts;
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + cos(theta) * aradius,
+ yc + sin(theta) * aradius);
+ }
+ art_vpath_add_point(p_vpath, pn, pn_max,
+ ART_LINETO, xc + x1, yc + y1);
+}
+
+/* Assume that forw and rev are at point i0. Bring them to i1,
+ joining with the vector i1 - i2.
+
+ This used to be true, but isn't now that the stroke_raw code is
+ filtering out (near)zero length vectors: {It so happens that all
+ invocations of this function maintain the precondition i1 = i0 + 1,
+ so we could decrease the number of arguments by one. We haven't
+ done that here, though.}
+
+ forw is to the line's right and rev is to its left.
+
+ Precondition: no zero-length vectors, otherwise a divide by
+ zero will happen. */
+static void render_seg(ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
+ ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
+ ArtVpath *vpath, int i0, int i1, int i2,
+ ArtPathStrokeJoinType join,
+ double line_width, double miter_limit, double flatness) {
+ double dx0, dy0;
+ double dx1, dy1;
+ double dlx0, dly0;
+ double dlx1, dly1;
+ double dmx, dmy;
+ double dmr2;
+ double scale;
+ double cross;
+
+ /* The vectors of the lines from i0 to i1 and i1 to i2. */
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ dx1 = vpath[i2].x - vpath[i1].x;
+ dy1 = vpath[i2].y - vpath[i1].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx1 * dx1 + dy1 * dy1);
+ dlx1 = dy1 * scale;
+ dly1 = -dx1 * scale;
+
+ /* now, forw's last point is expected to be colinear along d[xy]0
+ to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
+
+ /* positive for positive area (i.e. left turn) */
+ cross = dx1 * dy0 - dx0 * dy1;
+
+ dmx = (dlx0 + dlx1) * 0.5;
+ dmy = (dly0 + dly1) * 0.5;
+ dmr2 = dmx * dmx + dmy * dmy;
+
+ if (join == ART_PATH_STROKE_JOIN_MITER &&
+ dmr2 * miter_limit * miter_limit < line_width * line_width)
+ join = ART_PATH_STROKE_JOIN_BEVEL;
+
+ /* the case when dmr2 is zero or very small bothers me
+ (i.e. near a 180 degree angle)
+ ALEX: So, we avoid the optimization when dmr2 is very small. This should
+ be safe since dmx/y is only used in optimization and in MITER case, and MITER
+ should be converted to BEVEL when dmr2 is very small. */
+ if (dmr2 > EPSILON_2) {
+ scale = line_width * line_width / dmr2;
+ dmx *= scale;
+ dmy *= scale;
+ }
+
+ if (cross *cross < EPSILON_2 && dx0 *dx1 + dy0 *dy1 >= 0) {
+ /* going straight */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ } else if (cross > 0) {
+ /* left turn, forw is outside and rev is inside */
+
+ if (
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 + dm[xy] is inside i0-i1 rectangle */
+ (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
+ /* and that i1 + dm[xy] is inside i1-i2 rectangle */
+ ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
+ /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
+#endif
+ ) {
+ /* can safely add single intersection point */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ } else {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+ /* bevel */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ } else if (join == ART_PATH_STROKE_JOIN_MITER) {
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ } else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc(p_forw, pn_forw, pn_forw_max,
+ vpath[i1].x, vpath[i1].y,
+ -dlx0, -dly0,
+ -dlx1, -dly1,
+ line_width,
+ flatness);
+ } else {
+ /* right turn, rev is outside and forw is inside */
+
+ if (
+ (dmr2 > EPSILON_2) &&
+ /* check that i1 - dm[xy] is inside i0-i1 rectangle */
+ (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
+ /* and that i1 - dm[xy] is inside i1-i2 rectangle */
+ ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+ &&
+ /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
+ (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
+ /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
+ ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
+#endif
+ ) {
+ /* can safely add single intersection point */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+ } else {
+ /* need to loop-de-loop the inside */
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x, vpath[i1].y);
+ art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+ ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+ }
+
+ if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+ /* bevel */
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+ } else if (join == ART_PATH_STROKE_JOIN_MITER) {
+ art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+ ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+ } else if (join == ART_PATH_STROKE_JOIN_ROUND)
+ art_svp_vpath_stroke_arc(p_rev, pn_rev, pn_rev_max,
+ vpath[i1].x, vpath[i1].y,
+ dlx0, dly0,
+ dlx1, dly1,
+ -line_width,
+ flatness);
+
+ }
+}
+
+/* caps i1, under the assumption of a vector from i0 */
+static void render_cap(ArtVpath **p_result, int *pn_result, int *pn_result_max,
+ ArtVpath *vpath, int i0, int i1,
+ ArtPathStrokeCapType cap, double line_width, double flatness) {
+ double dx0, dy0;
+ double dlx0, dly0;
+ double scale;
+ int n_pts;
+ int i;
+
+ dx0 = vpath[i1].x - vpath[i0].x;
+ dy0 = vpath[i1].y - vpath[i0].y;
+
+ /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+ 90 degrees, and scaled to the length of line_width. */
+ scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+ dlx0 = dy0 * scale;
+ dly0 = -dx0 * scale;
+
+ switch (cap) {
+ case ART_PATH_STROKE_CAP_BUTT:
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_ROUND:
+ n_pts = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+ for (i = 1; i < n_pts; i++) {
+ double theta, c_th, s_th;
+
+ theta = M_PI * i / n_pts;
+ c_th = cos(theta);
+ s_th = sin(theta);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 * c_th - dly0 * s_th,
+ vpath[i1].y - dly0 * c_th + dlx0 * s_th);
+ }
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+ break;
+ case ART_PATH_STROKE_CAP_SQUARE:
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x - dlx0 - dly0,
+ vpath[i1].y - dly0 + dlx0);
+ art_vpath_add_point(p_result, pn_result, pn_result_max,
+ ART_LINETO,
+ vpath[i1].x + dlx0 - dly0,
+ vpath[i1].y + dly0 + dlx0);
+ break;
+ }
+}
+
+/**
+ * art_svp_from_vpath_raw: Stroke a vector path, raw version
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Exactly the same as art_svp_vpath_stroke(), except that the resulting
+ * stroke outline may self-intersect and have regions of winding number
+ * greater than 1.
+ *
+ * Return value: Resulting raw stroked outline in svp format.
+ **/
+ArtVpath *art_svp_vpath_stroke_raw(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness) {
+ int begin_idx, end_idx;
+ int i;
+ ArtVpath *forw, *rev;
+ int n_forw, n_rev;
+ int n_forw_max, n_rev_max;
+ ArtVpath *result;
+ int n_result, n_result_max;
+ double half_lw = 0.5 * line_width;
+ int closed;
+ int last, this_, next, second;
+ double dx, dy;
+
+ n_forw_max = 16;
+ forw = art_new(ArtVpath, n_forw_max);
+
+ n_rev_max = 16;
+ rev = art_new(ArtVpath, n_rev_max);
+
+ n_result = 0;
+ n_result_max = 16;
+ result = art_new(ArtVpath, n_result_max);
+
+ for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx) {
+ n_forw = 0;
+ n_rev = 0;
+
+ closed = (vpath[begin_idx].code == ART_MOVETO);
+
+ /* we don't know what the first point joins with until we get to the
+ last point and see if it's closed. So we start with the second
+ line in the path.
+
+ Note: this is not strictly true (we now know it's closed from
+ the opening pathcode), but why fix code that isn't broken?
+ */
+
+ this_ = begin_idx;
+ /* skip over identical points at the beginning of the subpath */
+ for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+ dx = vpath[i].x - vpath[this_].x;
+ dy = vpath[i].y - vpath[this_].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ second = next;
+
+ /* invariant: this doesn't coincide with next */
+ while (vpath[next].code == ART_LINETO) {
+ last = this_;
+ this_ = next;
+ /* skip over identical points after the beginning of the subpath */
+ for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+ dx = vpath[i].x - vpath[this_].x;
+ dy = vpath[i].y - vpath[this_].y;
+ if (dx * dx + dy * dy > EPSILON_2)
+ break;
+ }
+ next = i;
+ if (vpath[next].code != ART_LINETO) {
+ /* reached end of path */
+ /* make "closed" detection conform to PostScript
+ semantics (i.e. explicit closepath code rather than
+ just the fact that end of the path is the beginning) */
+ if (closed &&
+ vpath[this_].x == vpath[begin_idx].x &&
+ vpath[this_].y == vpath[begin_idx].y) {
+ int j;
+
+ /* path is closed, render join to beginning */
+ render_seg(&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this_, second,
+ join, half_lw, miter_limit, flatness);
+
+ /* do forward path */
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[n_forw - 1].x,
+ forw[n_forw - 1].y);
+ for (j = 0; j < n_forw; j++)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+
+ /* do reverse path, reversed */
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, rev[0].x,
+ rev[0].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ } else {
+ /* path is open */
+ int j;
+
+ /* add to forw rather than result to ensure that
+ forw has at least one point. */
+ render_cap(&forw, &n_forw, &n_forw_max,
+ vpath, last, this_,
+ cap, half_lw, flatness);
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_MOVETO, forw[0].x,
+ forw[0].y);
+ for (j = 1; j < n_forw; j++)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[j].x,
+ forw[j].y);
+ for (j = n_rev - 1; j >= 0; j--)
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, rev[j].x,
+ rev[j].y);
+ render_cap(&result, &n_result, &n_result_max,
+ vpath, second, begin_idx,
+ cap, half_lw, flatness);
+ art_vpath_add_point(&result, &n_result, &n_result_max,
+ ART_LINETO, forw[0].x,
+ forw[0].y);
+ }
+ } else
+ render_seg(&forw, &n_forw, &n_forw_max,
+ &rev, &n_rev, &n_rev_max,
+ vpath, last, this_, next,
+ join, half_lw, miter_limit, flatness);
+ }
+ end_idx = next;
+ }
+
+ free(forw);
+ free(rev);
+ art_vpath_add_point(&result, &n_result, &n_result_max, ART_END, 0, 0);
+ return result;
+}
+
+
+/* Render a vector path into a stroked outline.
+
+ Status of this routine:
+
+ Basic correctness: Only miter and bevel line joins are implemented,
+ and only butt line caps. Otherwise, seems to be fine.
+
+ Numerical stability: We cheat (adding random perturbation). Thus,
+ it seems very likely that no numerical stability problems will be
+ seen in practice.
+
+ Speed: Should be pretty good.
+
+ Precision: The perturbation fuzzes the coordinates slightly,
+ but not enough to be visible. */
+
+/**
+ * art_svp_vpath_stroke: Stroke a vector path.
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Computes an svp representing the stroked outline of @vpath. The
+ * width of the stroked line is @line_width.
+ *
+ * Lines are joined according to the @join rule. Possible values are
+ * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
+ * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
+ * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
+ * is converted to a bevelled join if the miter would extend to a
+ * distance of more than @miter_limit * @line_width from the actual
+ * join point.
+ *
+ * If there are open subpaths, the ends of these subpaths are capped
+ * according to the @cap rule. Possible values are
+ * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
+ * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
+ * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
+ * extending half @line_width past the end point).
+ *
+ * The @flatness parameter controls the accuracy of the rendering. It
+ * is most important for determining the number of points to use to
+ * approximate circular arcs for round lines and joins. In general, the
+ * resulting vector path will be within @flatness pixels of the "ideal"
+ * path containing actual circular arcs. I reserve the right to use
+ * the @flatness parameter to convert bevelled joins to miters for very
+ * small turn angles, as this would reduce the number of points in the
+ * resulting outline path.
+ *
+ * The resulting path is "clean" with respect to self-intersections, i.e.
+ * the winding number is 0 or 1 at each point.
+ *
+ * Return value: Resulting stroked outline in svp format.
+ **/
+ArtSVP *art_svp_vpath_stroke(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness) {
+ ArtVpath *vpath_stroke;
+ ArtSVP *svp, *svp2;
+ ArtSvpWriter *swr;
+
+ vpath_stroke = art_svp_vpath_stroke_raw(vpath, join, cap,
+ line_width, miter_limit, flatness);
+ svp = art_svp_from_vpath(vpath_stroke);
+ free(vpath_stroke);
+
+ swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO);
+ art_svp_intersector(svp, swr);
+
+ svp2 = art_svp_writer_rewind_reap(swr);
+ art_svp_free(svp);
+ return svp2;
+}
+
+
+/* Testbed implementation of the new intersection code.
+*/
+
+typedef struct _ArtPriQ ArtPriQ;
+typedef struct _ArtPriPoint ArtPriPoint;
+
+struct _ArtPriQ {
+ int n_items;
+ int n_items_max;
+ ArtPriPoint **items;
+};
+
+struct _ArtPriPoint {
+ double x;
+ double y;
+ void *user_data;
+};
+
+static ArtPriQ *art_pri_new(void) {
+ ArtPriQ *result = art_new(ArtPriQ, 1);
+
+ result->n_items = 0;
+ result->n_items_max = 16;
+ result->items = art_new(ArtPriPoint *, result->n_items_max);
+ return result;
+}
+
+static void art_pri_free(ArtPriQ *pq) {
+ free(pq->items);
+ free(pq);
+}
+
+static art_boolean art_pri_empty(ArtPriQ *pq) {
+ return pq->n_items == 0;
+}
+
+/* This heap implementation is based on Vasek Chvatal's course notes:
+ http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
+
+static void art_pri_bubble_up(ArtPriQ *pq, int vacant, ArtPriPoint *missing) {
+ ArtPriPoint **items = pq->items;
+ int parent;
+
+ parent = (vacant - 1) >> 1;
+ while (vacant > 0 && (missing->y < items[parent]->y ||
+ (missing->y == items[parent]->y &&
+ missing->x < items[parent]->x))) {
+ items[vacant] = items[parent];
+ vacant = parent;
+ parent = (vacant - 1) >> 1;
+ }
+
+ items[vacant] = missing;
+}
+
+static void art_pri_insert(ArtPriQ *pq, ArtPriPoint *point) {
+ if (pq->n_items == pq->n_items_max)
+ art_expand(pq->items, ArtPriPoint *, pq->n_items_max);
+
+ art_pri_bubble_up(pq, pq->n_items++, point);
+}
+
+static void art_pri_sift_down_from_root(ArtPriQ *pq, ArtPriPoint *missing) {
+ ArtPriPoint **items = pq->items;
+ int vacant = 0, child = 2;
+ int n = pq->n_items;
+
+ while (child < n) {
+ if (items[child - 1]->y < items[child]->y ||
+ (items[child - 1]->y == items[child]->y &&
+ items[child - 1]->x < items[child]->x))
+ child--;
+ items[vacant] = items[child];
+ vacant = child;
+ child = (vacant + 1) << 1;
+ }
+ if (child == n) {
+ items[vacant] = items[n - 1];
+ vacant = n - 1;
+ }
+
+ art_pri_bubble_up(pq, vacant, missing);
+}
+
+static ArtPriPoint *art_pri_choose(ArtPriQ *pq) {
+ ArtPriPoint *result = pq->items[0];
+
+ art_pri_sift_down_from_root(pq, pq->items[--pq->n_items]);
+ return result;
+}
+
+/* A virtual class for an "svp writer". A client of this object creates an
+ SVP by repeatedly calling "add segment" and "add point" methods on it.
+*/
+
+typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
+
+/* An implementation of the svp writer virtual class that applies the
+ winding rule. */
+
+struct _ArtSvpWriterRewind {
+ ArtSvpWriter super;
+ ArtWindRule rule;
+ ArtSVP *svp;
+ int n_segs_max;
+ int *n_points_max;
+};
+
+static int art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
+ int delta_wind, double x, double y) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *svp;
+ ArtSVPSeg *seg;
+ art_boolean left_filled, right_filled;
+ int wind_right = wind_left + delta_wind;
+ int seg_num;
+ const int init_n_points_max = 4;
+
+ switch (swr->rule) {
+ case ART_WIND_RULE_NONZERO:
+ left_filled = (wind_left != 0);
+ right_filled = (wind_right != 0);
+ break;
+ case ART_WIND_RULE_INTERSECT:
+ left_filled = (wind_left > 1);
+ right_filled = (wind_right > 1);
+ break;
+ case ART_WIND_RULE_ODDEVEN:
+ left_filled = (wind_left & 1);
+ right_filled = (wind_right & 1);
+ break;
+ case ART_WIND_RULE_POSITIVE:
+ left_filled = (wind_left > 0);
+ right_filled = (wind_right > 0);
+ break;
+ default:
+ art_die("Unknown wind rule %d\n", swr->rule);
+ }
+ if (left_filled == right_filled) {
+ /* discard segment now */
+ return -1;
+ }
+
+ svp = swr->svp;
+ seg_num = svp->n_segs++;
+ if (swr->n_segs_max == seg_num) {
+ swr->n_segs_max <<= 1;
+ svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+ (swr->n_segs_max - 1) *
+ sizeof(ArtSVPSeg));
+ swr->svp = svp;
+ swr->n_points_max = art_renew(swr->n_points_max, int,
+ swr->n_segs_max);
+ }
+ seg = &svp->segs[seg_num];
+ seg->n_points = 1;
+ seg->dir = right_filled;
+ swr->n_points_max[seg_num] = init_n_points_max;
+ seg->bbox.x0 = x;
+ seg->bbox.y0 = y;
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+ seg->points = art_new(ArtPoint, init_n_points_max);
+ seg->points[0].x = x;
+ seg->points[0].y = y;
+ return seg_num;
+}
+
+static void art_svp_writer_rewind_add_point(ArtSvpWriter *self, int seg_id,
+ double x, double y) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVPSeg *seg;
+ int n_points;
+
+ if (seg_id < 0)
+ /* omitted segment */
+ return;
+
+ seg = &swr->svp->segs[seg_id];
+ n_points = seg->n_points++;
+ if (swr->n_points_max[seg_id] == n_points)
+ art_expand(seg->points, ArtPoint, swr->n_points_max[seg_id]);
+ seg->points[n_points].x = x;
+ seg->points[n_points].y = y;
+ if (x < seg->bbox.x0)
+ seg->bbox.x0 = x;
+ if (x > seg->bbox.x1)
+ seg->bbox.x1 = x;
+ seg->bbox.y1 = y;
+}
+
+static void art_svp_writer_rewind_close_segment(ArtSvpWriter *self, int seg_id) {
+ /* Not needed for this simple implementation. A potential future
+ optimization is to merge segments that can be merged safely. */
+}
+
+ArtSVP *art_svp_writer_rewind_reap(ArtSvpWriter *self) {
+ ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+ ArtSVP *result = swr->svp;
+
+ free(swr->n_points_max);
+ free(swr);
+ return result;
+}
+
+ArtSvpWriter *art_svp_writer_rewind_new(ArtWindRule rule) {
+ ArtSvpWriterRewind *result = art_new(ArtSvpWriterRewind, 1);
+
+ result->super.add_segment = art_svp_writer_rewind_add_segment;
+ result->super.add_point = art_svp_writer_rewind_add_point;
+ result->super.close_segment = art_svp_writer_rewind_close_segment;
+
+ result->rule = rule;
+ result->n_segs_max = 16;
+ result->svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+ (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
+ result->svp->n_segs = 0;
+ result->n_points_max = art_new(int, result->n_segs_max);
+
+ return &result->super;
+}
+
+/* Now, data structures for the active list */
+
+typedef struct _ArtActiveSeg ArtActiveSeg;
+
+/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
+ x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
+#define ART_ACTIVE_FLAGS_BNEG 1
+
+/* This flag is set if the segment has been inserted into the active
+ list. */
+#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
+
+/* This flag is set when the segment is to be deleted in the
+ horiz commit process. */
+#define ART_ACTIVE_FLAGS_DEL 4
+
+/* This flag is set if the seg_id is a valid output segment. */
+#define ART_ACTIVE_FLAGS_OUT 8
+
+/* This flag is set if the segment is in the horiz list. */
+#define ART_ACTIVE_FLAGS_IN_HORIZ 16
+
+struct _ArtActiveSeg {
+ int flags;
+ int wind_left, delta_wind;
+ ArtActiveSeg *left, *right; /* doubly linked list structure */
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+
+ double x[2];
+ double y0, y1;
+ double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
+ and a>0 */
+
+ /* bottom point and intersection point stack */
+ int n_stack;
+ int n_stack_max;
+ ArtPoint *stack;
+
+ /* horiz commit list */
+ ArtActiveSeg *horiz_left, *horiz_right;
+ double horiz_x;
+ int horiz_delta_wind;
+ int seg_id;
+};
+
+typedef struct _ArtIntersectCtx ArtIntersectCtx;
+
+struct _ArtIntersectCtx {
+ const ArtSVP *in;
+ ArtSvpWriter *out;
+
+ ArtPriQ *pq;
+
+ ArtActiveSeg *active_head;
+
+ double y;
+ ArtActiveSeg *horiz_first;
+ ArtActiveSeg *horiz_last;
+
+ /* segment index of next input segment to be added to pri q */
+ int in_curs;
+};
+
+#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
+
+/**
+ * art_svp_intersect_setup_seg: Set up an active segment from input segment.
+ * @seg: Active segment.
+ * @pri_pt: Priority queue point to initialize.
+ *
+ * Sets the x[], a, b, c, flags, and stack fields according to the
+ * line from the current cursor value. Sets the priority queue point
+ * to the bottom point of this line. Also advances the input segment
+ * cursor.
+ **/
+static void art_svp_intersect_setup_seg(ArtActiveSeg *seg, ArtPriPoint *pri_pt) {
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs++;
+ double x0, y0, x1, y1;
+ double dx, dy, s;
+ double a, b, r2;
+
+ x0 = in_seg->points[in_curs].x;
+ y0 = in_seg->points[in_curs].y;
+ x1 = in_seg->points[in_curs + 1].x;
+ y1 = in_seg->points[in_curs + 1].y;
+ pri_pt->x = x1;
+ pri_pt->y = y1;
+ dx = x1 - x0;
+ dy = y1 - y0;
+ r2 = dx * dx + dy * dy;
+ s = r2 == 0 ? 1 : 1 / sqrt(r2);
+ seg->a = a = dy * s;
+ seg->b = b = -dx * s;
+ seg->c = -(a * x0 + b * y0);
+ seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
+ seg->x[0] = x0;
+ seg->x[1] = x1;
+ seg->y0 = y0;
+ seg->y1 = y1;
+ seg->n_stack = 1;
+ seg->stack[0].x = x1;
+ seg->stack[0].y = y1;
+}
+
+/**
+ * art_svp_intersect_add_horiz: Add point to horizontal list.
+ * @ctx: Intersector context.
+ * @seg: Segment with point to insert into horizontal list.
+ *
+ * Inserts @seg into horizontal list, keeping it in ascending horiz_x
+ * order.
+ *
+ * Note: the horiz_commit routine processes "clusters" of segs in the
+ * horiz list, all sharing the same horiz_x value. The cluster is
+ * processed in active list order, rather than horiz list order. Thus,
+ * the order of segs in the horiz list sharing the same horiz_x
+ * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
+ * as a "belt and suspenders" defensive coding tactic.
+ **/
+static void art_svp_intersect_add_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ ArtActiveSeg **pp = &ctx->horiz_last;
+ ArtActiveSeg *place;
+ ArtActiveSeg *place_right = NULL;
+
+ if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) {
+ art_warn("*** attempt to put segment in horiz list twice\n");
+ return;
+ }
+ seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
+
+ for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
+ (place->horiz_x == seg->horiz_x &&
+ place->b < seg->b));
+ place = *pp) {
+ place_right = place;
+ pp = &place->horiz_left;
+ }
+ *pp = seg;
+ seg->horiz_left = place;
+ seg->horiz_right = place_right;
+ if (place == NULL)
+ ctx->horiz_first = seg;
+ else
+ place->horiz_right = seg;
+}
+
+static void art_svp_intersect_push_pt(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x, double y) {
+ ArtPriPoint *pri_pt;
+ int n_stack = seg->n_stack;
+
+ if (n_stack == seg->n_stack_max)
+ art_expand(seg->stack, ArtPoint, seg->n_stack_max);
+ seg->stack[n_stack].x = x;
+ seg->stack[n_stack].y = y;
+ seg->n_stack++;
+
+ seg->x[1] = x;
+ seg->y1 = y;
+
+ pri_pt = art_new(ArtPriPoint, 1);
+ pri_pt->x = x;
+ pri_pt->y = y;
+ pri_pt->user_data = seg;
+ art_pri_insert(ctx->pq, pri_pt);
+}
+
+typedef enum {
+ ART_BREAK_LEFT = 1,
+ ART_BREAK_RIGHT = 2
+} ArtBreakFlags;
+
+/**
+ * art_svp_intersect_break: Break an active segment.
+ *
+ * Note: y must be greater than the top point's y, and less than
+ * the bottom's.
+ *
+ * Return value: x coordinate of break point.
+ */
+static double art_svp_intersect_break(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x_ref, double y, ArtBreakFlags break_flags) {
+ double x0, y0, x1, y1;
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ double x;
+
+ x0 = in_seg->points[in_curs - 1].x;
+ y0 = in_seg->points[in_curs - 1].y;
+ x1 = in_seg->points[in_curs].x;
+ y1 = in_seg->points[in_curs].y;
+ x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
+ if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
+ (break_flags == ART_BREAK_RIGHT && x < x_ref)) {
+ }
+
+ /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
+ arithmetic, but it might be worthwhile to check just in case. */
+
+ if (y > ctx->y)
+ art_svp_intersect_push_pt(ctx, seg, x, y);
+ else {
+ seg->x[0] = x;
+ seg->y0 = y;
+ seg->horiz_x = x;
+ art_svp_intersect_add_horiz(ctx, seg);
+ }
+
+ return x;
+}
+
+/**
+ * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
+ * @ctx: Intersector context.
+ * @x: X coordinate of point to add.
+ * @y: Y coordinate of point to add.
+ * @seg: "nearby" segment, or NULL if leftmost.
+ *
+ * Return value: Segment immediately to the left of the new point, or
+ * NULL if the new point is leftmost.
+ **/
+static ArtActiveSeg *art_svp_intersect_add_point(ArtIntersectCtx *ctx, double x, double y,
+ ArtActiveSeg *seg, ArtBreakFlags break_flags) {
+ ArtActiveSeg *left, *right;
+ double x_min = x, x_max = x;
+ art_boolean left_live, right_live;
+ double d;
+ double new_x;
+ ArtActiveSeg *test, *result = NULL;
+ double x_test;
+
+ left = seg;
+ if (left == NULL)
+ right = ctx->active_head;
+ else
+ right = left->right;
+ left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
+ right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
+ while (left_live || right_live) {
+ if (left_live) {
+ if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != left->y0 && y < left->y1) {
+ d = x_min * left->a + y * left->b + left->c;
+ if (d < EPSILON_A) {
+ new_x = art_svp_intersect_break(ctx, left, x_min, y,
+ ART_BREAK_LEFT);
+ if (new_x > x_max) {
+ x_max = new_x;
+ right_live = (right != NULL);
+ } else if (new_x < x_min)
+ x_min = new_x;
+ left = left->left;
+ left_live = (left != NULL);
+ } else
+ left_live = ART_FALSE;
+ } else
+ left_live = ART_FALSE;
+ } else if (right_live) {
+ if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
+ /* It may be that one of these conjuncts turns out to be always
+ true. We test both anyway, to be defensive. */
+ y != right->y0 && y < right->y1) {
+ d = x_max * right->a + y * right->b + right->c;
+ if (d > -EPSILON_A) {
+ new_x = art_svp_intersect_break(ctx, right, x_max, y,
+ ART_BREAK_RIGHT);
+ if (new_x < x_min) {
+ x_min = new_x;
+ left_live = (left != NULL);
+ } else if (new_x >= x_max)
+ x_max = new_x;
+ right = right->right;
+ right_live = (right != NULL);
+ } else
+ right_live = ART_FALSE;
+ } else
+ right_live = ART_FALSE;
+ }
+ }
+
+ /* Ascending order is guaranteed by break_flags. Thus, we don't need
+ to actually fix up non-ascending pairs. */
+
+ /* Now, (left, right) defines an interval of segments broken. Sort
+ into ascending x order. */
+ test = left == NULL ? ctx->active_head : left->right;
+ result = left;
+ if (test != NULL && test != right) {
+ if (y == test->y0)
+ x_test = test->x[0];
+ else /* assert y == test->y1, I think */
+ x_test = test->x[1];
+ for (;;) {
+ if (x_test <= x)
+ result = test;
+ test = test->right;
+ if (test == right)
+ break;
+ new_x = x_test;
+ if (new_x < x_test) {
+ art_warn("art_svp_intersect_add_point: non-ascending x\n");
+ }
+ x_test = new_x;
+ }
+ }
+ return result;
+}
+
+static void art_svp_intersect_swap_active(ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg) {
+ right_seg->left = left_seg->left;
+ if (right_seg->left != NULL)
+ right_seg->left->right = right_seg;
+ else
+ ctx->active_head = right_seg;
+ left_seg->right = right_seg->right;
+ if (left_seg->right != NULL)
+ left_seg->right->left = left_seg;
+ left_seg->left = right_seg;
+ right_seg->right = left_seg;
+}
+
+/**
+ * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
+ * @ctx: Intersector context.
+ * @left_seg: Left segment of the pair.
+ * @right_seg: Right segment of the pair.
+ * @break_flags: Flags indicating whether to break neighbors.
+ *
+ * Tests crossing of @left_seg and @right_seg. If there is a crossing,
+ * inserts the intersection point into both segments.
+ *
+ * Return value: True if the intersection took place at the current
+ * scan line, indicating further iteration is needed.
+ **/
+static art_boolean art_svp_intersect_test_cross(ArtIntersectCtx *ctx,
+ ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
+ ArtBreakFlags break_flags) {
+ double left_x0, left_y0, left_x1;
+ double left_y1 = left_seg->y1;
+ double right_y1 = right_seg->y1;
+ double d;
+
+ const ArtSVPSeg *in_seg;
+ int in_curs;
+ double d0, d1, t;
+ double x, y; /* intersection point */
+
+ if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0]) {
+ /* Top points of left and right segments coincide. This case
+ feels like a bit of duplication - we may want to merge it
+ with the cases below. However, this way, we're sure that this
+ logic makes only localized changes. */
+
+ if (left_y1 < right_y1) {
+ /* Test left (x1, y1) against right segment */
+ left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A) {
+ /* I'm unsure about the break flags here. */
+ double right_x1 = art_svp_intersect_break(ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else if (left_y1 > right_y1) {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A) {
+ /* See above regarding break flags. */
+ left_x1 = art_svp_intersect_break(ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else { /* left_y1 == right_y1 */
+ left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+
+ if (left_y1 < right_y1) {
+ /* Test left (x1, y1) against right segment */
+ left_x1 = left_seg->x[1];
+
+ if (left_x1 <
+ right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+ left_y1 == right_seg->y0)
+ return ART_FALSE;
+ d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d < -EPSILON_A)
+ return ART_FALSE;
+ else if (d < EPSILON_A) {
+ double right_x1 = art_svp_intersect_break(ctx, right_seg,
+ left_x1, left_y1,
+ ART_BREAK_RIGHT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else if (left_y1 > right_y1) {
+ /* Test right (x1, y1) against left segment */
+ double right_x1 = right_seg->x[1];
+
+ if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+ right_y1 == left_seg->y0)
+ return ART_FALSE;
+ d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+ if (d > EPSILON_A)
+ return ART_FALSE;
+ else if (d > -EPSILON_A) {
+ left_x1 = art_svp_intersect_break(ctx, left_seg,
+ right_x1, right_y1,
+ ART_BREAK_LEFT);
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+ } else { /* left_y1 == right_y1 */
+ left_x1 = left_seg->x[1];
+ double right_x1 = right_seg->x[1];
+
+ if (left_x1 <= right_x1)
+ return ART_FALSE;
+ }
+
+ /* The segments cross. Find the intersection point. */
+
+ in_seg = left_seg->in_seg;
+ in_curs = left_seg->in_curs;
+ left_x0 = in_seg->points[in_curs - 1].x;
+ left_y0 = in_seg->points[in_curs - 1].y;
+ left_x1 = in_seg->points[in_curs].x;
+ left_y1 = in_seg->points[in_curs].y;
+ d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
+ d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+ if (d0 == d1) {
+ x = left_x0;
+ y = left_y0;
+ } else {
+ /* Is this division always safe? It could possibly overflow. */
+ t = d0 / (d0 - d1);
+ if (t <= 0) {
+ x = left_x0;
+ y = left_y0;
+ } else if (t >= 1) {
+ x = left_x1;
+ y = left_y1;
+ } else {
+ x = left_x0 + t * (left_x1 - left_x0);
+ y = left_y0 + t * (left_y1 - left_y0);
+ }
+ }
+
+ /* Make sure intersection point is within bounds of right seg. */
+ if (y < right_seg->y0) {
+ x = right_seg->x[0];
+ y = right_seg->y0;
+ } else if (y > right_seg->y1) {
+ x = right_seg->x[1];
+ y = right_seg->y1;
+ } else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
+ x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
+ else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
+ x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
+
+ if (y == left_seg->y0) {
+ if (y != right_seg->y0) {
+ art_svp_intersect_push_pt(ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point(ctx, x, y, right_seg->right,
+ break_flags);
+ } else {
+ /* Intersection takes place at current scan line; process
+ immediately rather than queueing intersection point into
+ priq. */
+ ArtActiveSeg *winner, *loser;
+
+ /* Choose "most vertical" segement */
+ if (left_seg->a > right_seg->a) {
+ winner = left_seg;
+ loser = right_seg;
+ } else {
+ winner = right_seg;
+ loser = left_seg;
+ }
+
+ loser->x[0] = winner->x[0];
+ loser->horiz_x = loser->x[0];
+ loser->horiz_delta_wind += loser->delta_wind;
+ winner->horiz_delta_wind -= loser->delta_wind;
+
+ art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+ return ART_TRUE;
+ }
+ } else if (y == right_seg->y0) {
+ art_svp_intersect_push_pt(ctx, left_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point(ctx, x, y, left_seg->left,
+ break_flags);
+ } else {
+ /* Insert the intersection point into both segments. */
+ art_svp_intersect_push_pt(ctx, left_seg, x, y);
+ art_svp_intersect_push_pt(ctx, right_seg, x, y);
+ if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+ art_svp_intersect_add_point(ctx, x, y, left_seg->left, break_flags);
+ if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+ art_svp_intersect_add_point(ctx, x, y, right_seg->right, break_flags);
+ }
+ return ART_FALSE;
+}
+
+/**
+ * art_svp_intersect_active_delete: Delete segment from active list.
+ * @ctx: Intersection context.
+ * @seg: Segment to delete.
+ *
+ * Deletes @seg from the active list.
+ **/
+static void art_svp_intersect_active_delete(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+ if (left != NULL)
+ left->right = right;
+ else
+ ctx->active_head = right;
+ if (right != NULL)
+ right->left = left;
+}
+
+/**
+ * art_svp_intersect_active_free: Free an active segment.
+ * @seg: Segment to delete.
+ *
+ * Frees @seg.
+ **/
+static void art_svp_intersect_active_free(ArtActiveSeg *seg) {
+ free(seg->stack);
+ free(seg);
+}
+
+/**
+ * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
+ *
+ * Tests @seg against its left and right neighbors for intersections.
+ * Precondition: the line in @seg is not purely horizontal.
+ **/
+static void art_svp_intersect_insert_cross(ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg) {
+ ArtActiveSeg *left = seg, *right = seg;
+
+ for (;;) {
+ if (left != NULL) {
+ ArtActiveSeg *leftc;
+
+ for (leftc = left->left; leftc != NULL; leftc = leftc->left)
+ if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (leftc != NULL &&
+ art_svp_intersect_test_cross(ctx, leftc, left,
+ ART_BREAK_LEFT)) {
+ if (left == right || right == NULL)
+ right = left->right;
+ } else {
+ left = NULL;
+ }
+ } else if (right != NULL && right->right != NULL) {
+ ArtActiveSeg *rightc;
+
+ for (rightc = right->right; rightc != NULL; rightc = rightc->right)
+ if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+ if (rightc != NULL &&
+ art_svp_intersect_test_cross(ctx, right, rightc,
+ ART_BREAK_RIGHT)) {
+ if (left == right || left == NULL)
+ left = right->left;
+ } else {
+ right = NULL;
+ }
+ } else
+ break;
+ }
+}
+
+/**
+ * art_svp_intersect_horiz: Add horizontal line segment.
+ * @ctx: Intersector context.
+ * @seg: Segment on which to add horizontal line.
+ * @x0: Old x position.
+ * @x1: New x position.
+ *
+ * Adds a horizontal line from @x0 to @x1, and updates the current
+ * location of @seg to @x1.
+ **/
+static void art_svp_intersect_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ double x0, double x1) {
+ ArtActiveSeg *hs;
+
+ if (x0 == x1)
+ return;
+
+ hs = art_new(ArtActiveSeg, 1);
+
+ hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+ ArtSvpWriter *swr = ctx->out;
+
+ swr->add_point(swr, seg->seg_id, x0, ctx->y);
+ }
+ hs->seg_id = seg->seg_id;
+ hs->horiz_x = x0;
+ hs->horiz_delta_wind = seg->delta_wind;
+ hs->stack = NULL;
+
+ /* Ideally, the (a, b, c) values will never be read. However, there
+ are probably some tests remaining that don't check for _DEL
+ before evaluating the line equation. For those, these
+ initializations will at least prevent a UMR of the values, which
+ can crash on some platforms. */
+ hs->a = 0.0;
+ hs->b = 0.0;
+ hs->c = 0.0;
+
+ seg->horiz_delta_wind -= seg->delta_wind;
+
+ art_svp_intersect_add_horiz(ctx, hs);
+
+ if (x0 > x1) {
+ ArtActiveSeg *left;
+ art_boolean first = ART_TRUE;
+
+ for (left = seg->left; left != NULL; left = seg->left) {
+ int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (left->x[left_bneg] <= x1)
+ break;
+ if (left->x[left_bneg ^ 1] <= x1 &&
+ x1 *left->a + ctx->y *left->b + left->c >= 0)
+ break;
+ if (left->y0 != ctx->y && left->y1 != ctx->y) {
+ art_svp_intersect_break(ctx, left, x1, ctx->y, ART_BREAK_LEFT);
+ }
+ art_svp_intersect_swap_active(ctx, left, seg);
+ if (first && left->right != NULL) {
+ art_svp_intersect_test_cross(ctx, left, left->right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ } else {
+ ArtActiveSeg *right;
+ art_boolean first = ART_TRUE;
+
+ for (right = seg->right; right != NULL; right = seg->right) {
+ int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (right->x[right_bneg ^ 1] >= x1)
+ break;
+ if (right->x[right_bneg] >= x1 &&
+ x1 *right->a + ctx->y *right->b + right->c <= 0)
+ break;
+ if (right->y0 != ctx->y && right->y1 != ctx->y) {
+ art_svp_intersect_break(ctx, right, x1, ctx->y,
+ ART_BREAK_LEFT);
+ }
+ art_svp_intersect_swap_active(ctx, seg, right);
+ if (first && right->left != NULL) {
+ art_svp_intersect_test_cross(ctx, right->left, right,
+ ART_BREAK_RIGHT);
+ first = ART_FALSE;
+ }
+ }
+ }
+
+ seg->x[0] = x1;
+ seg->x[1] = x1;
+ seg->horiz_x = x1;
+ seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+}
+
+/**
+ * art_svp_intersect_insert_line: Insert a line into the active list.
+ * @ctx: Intersector context.
+ * @seg: Segment containing line to insert.
+ *
+ * Inserts the line into the intersector context, taking care of any
+ * intersections, and adding the appropriate horizontal points to the
+ * active list.
+ **/
+static void art_svp_intersect_insert_line(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+ if (seg->y1 == seg->y0) {
+ art_svp_intersect_horiz(ctx, seg, seg->x[0], seg->x[1]);
+ } else {
+ art_svp_intersect_insert_cross(ctx, seg);
+ art_svp_intersect_add_horiz(ctx, seg);
+ }
+}
+
+static void art_svp_intersect_process_intersection(ArtIntersectCtx *ctx,
+ ArtActiveSeg *seg) {
+ int n_stack = --seg->n_stack;
+ seg->x[1] = seg->stack[n_stack - 1].x;
+ seg->y1 = seg->stack[n_stack - 1].y;
+ seg->x[0] = seg->stack[n_stack].x;
+ seg->y0 = seg->stack[n_stack].y;
+ seg->horiz_x = seg->x[0];
+ art_svp_intersect_insert_line(ctx, seg);
+}
+
+static void art_svp_intersect_advance_cursor(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+ ArtPriPoint *pri_pt) {
+ const ArtSVPSeg *in_seg = seg->in_seg;
+ int in_curs = seg->in_curs;
+ ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
+
+ if (swr != NULL)
+ swr->add_point(swr, seg->seg_id, seg->x[1], seg->y1);
+ if (in_curs + 1 == in_seg->n_points) {
+ ArtActiveSeg *left = seg->left, *right = seg->right;
+
+ seg->flags |= ART_ACTIVE_FLAGS_DEL;
+ art_svp_intersect_add_horiz(ctx, seg);
+ art_svp_intersect_active_delete(ctx, seg);
+ if (left != NULL && right != NULL)
+ art_svp_intersect_test_cross(ctx, left, right,
+ (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+ free(pri_pt);
+ } else {
+ seg->horiz_x = seg->x[1];
+
+ art_svp_intersect_setup_seg(seg, pri_pt);
+ art_pri_insert(ctx->pq, pri_pt);
+ art_svp_intersect_insert_line(ctx, seg);
+ }
+}
+
+static void art_svp_intersect_add_seg(ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg) {
+ ArtActiveSeg *seg = art_new(ArtActiveSeg, 1);
+ ArtActiveSeg *test;
+ double x0, y0;
+ ArtActiveSeg *beg_range;
+ ArtActiveSeg *last = NULL;
+ ArtActiveSeg *left, *right;
+ ArtPriPoint *pri_pt = art_new(ArtPriPoint, 1);
+
+ seg->flags = 0;
+ seg->in_seg = in_seg;
+ seg->in_curs = 0;
+
+ seg->n_stack_max = 4;
+ seg->stack = art_new(ArtPoint, seg->n_stack_max);
+
+ seg->horiz_delta_wind = 0;
+
+ seg->wind_left = 0;
+
+ pri_pt->user_data = seg;
+ art_svp_intersect_setup_seg(seg, pri_pt);
+ art_pri_insert(ctx->pq, pri_pt);
+
+ /* Find insertion place for new segment */
+ /* This is currently a left-to-right scan, but should be replaced
+ with a binary search as soon as it's validated. */
+
+ x0 = in_seg->points[0].x;
+ y0 = in_seg->points[0].y;
+ beg_range = NULL;
+ for (test = ctx->active_head; test != NULL; test = test->right) {
+ double d;
+ int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
+
+ if (x0 < test->x[test_bneg]) {
+ if (x0 < test->x[test_bneg ^ 1])
+ break;
+ d = x0 * test->a + y0 * test->b + test->c;
+ if (d < 0)
+ break;
+ }
+ last = test;
+ }
+
+ left = art_svp_intersect_add_point(ctx, x0, y0, last, (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+ seg->left = left;
+ if (left == NULL) {
+ right = ctx->active_head;
+ ctx->active_head = seg;
+ } else {
+ right = left->right;
+ left->right = seg;
+ }
+ seg->right = right;
+ if (right != NULL)
+ right->left = seg;
+
+ seg->delta_wind = in_seg->dir ? 1 : -1;
+ seg->horiz_x = x0;
+
+ art_svp_intersect_insert_line(ctx, seg);
+}
+
+/**
+ * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
+ * @ctx: Intersection context.
+ *
+ * The main function of the horizontal commit is to output new
+ * points to the output writer.
+ *
+ * This "commit" pass is also where winding numbers are assigned,
+ * because doing it here provides much greater tolerance for inputs
+ * which are not in strict SVP order.
+ *
+ * Each cluster in the horiz_list contains both segments that are in
+ * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
+ * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
+ * need to deal with both.
+ **/
+static void art_svp_intersect_horiz_commit(ArtIntersectCtx *ctx) {
+ ArtActiveSeg *seg;
+ int winding_number = 0; /* initialization just to avoid warning */
+ int horiz_wind = 0;
+ double last_x = 0; /* initialization just to avoid warning */
+
+ /* Output points to svp writer. */
+ for (seg = ctx->horiz_first; seg != NULL;) {
+ /* Find a cluster with common horiz_x, */
+ ArtActiveSeg *curs;
+ double x = seg->horiz_x;
+
+ /* Generate any horizontal segments. */
+ if (horiz_wind != 0) {
+ ArtSvpWriter *swr = ctx->out;
+ int seg_id;
+
+ seg_id = swr->add_segment(swr, winding_number, horiz_wind,
+ last_x, ctx->y);
+ swr->add_point(swr, seg_id, x, ctx->y);
+ swr->close_segment(swr, seg_id);
+ }
+
+ /* Find first active segment in cluster. */
+
+ for (curs = seg; curs != NULL && curs->horiz_x == x;
+ curs = curs->horiz_right)
+ if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
+ break;
+
+ if (curs != NULL && curs->horiz_x == x) {
+ /* There exists at least one active segment in this cluster. */
+
+ /* Find beginning of cluster. */
+ for (; curs->left != NULL; curs = curs->left)
+ if (curs->left->horiz_x != x)
+ break;
+
+ if (curs->left != NULL)
+ winding_number = curs->left->wind_left + curs->left->delta_wind;
+ else
+ winding_number = 0;
+
+ do {
+ if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
+ curs->wind_left != winding_number) {
+ ArtSvpWriter *swr = ctx->out;
+
+ if (curs->flags & ART_ACTIVE_FLAGS_OUT) {
+ swr->add_point(swr, curs->seg_id,
+ curs->horiz_x, ctx->y);
+ swr->close_segment(swr, curs->seg_id);
+ }
+
+ curs->seg_id = swr->add_segment(swr, winding_number,
+ curs->delta_wind,
+ x, ctx->y);
+ curs->flags |= ART_ACTIVE_FLAGS_OUT;
+ }
+ curs->wind_left = winding_number;
+ winding_number += curs->delta_wind;
+ curs = curs->right;
+ } while (curs != NULL && curs->horiz_x == x);
+ }
+
+ /* Skip past cluster. */
+ do {
+ ArtActiveSeg *next = seg->horiz_right;
+
+ seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
+ horiz_wind += seg->horiz_delta_wind;
+ seg->horiz_delta_wind = 0;
+ if (seg->flags & ART_ACTIVE_FLAGS_DEL) {
+ if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+ ArtSvpWriter *swr = ctx->out;
+ swr->close_segment(swr, seg->seg_id);
+ }
+ art_svp_intersect_active_free(seg);
+ }
+ seg = next;
+ } while (seg != NULL && seg->horiz_x == x);
+
+ last_x = x;
+ }
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+}
+
+void art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out) {
+ ArtIntersectCtx *ctx;
+ ArtPriQ *pq;
+ ArtPriPoint *first_point;
+
+ if (in->n_segs == 0)
+ return;
+
+ ctx = art_new(ArtIntersectCtx, 1);
+ ctx->in = in;
+ ctx->out = out;
+ pq = art_pri_new();
+ ctx->pq = pq;
+
+ ctx->active_head = NULL;
+
+ ctx->horiz_first = NULL;
+ ctx->horiz_last = NULL;
+
+ ctx->in_curs = 0;
+ first_point = art_new(ArtPriPoint, 1);
+ first_point->x = in->segs[0].points[0].x;
+ first_point->y = in->segs[0].points[0].y;
+ first_point->user_data = NULL;
+ ctx->y = first_point->y;
+ art_pri_insert(pq, first_point);
+
+ while (!art_pri_empty(pq)) {
+ ArtPriPoint *pri_point = art_pri_choose(pq);
+ ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
+
+ if (ctx->y != pri_point->y) {
+ art_svp_intersect_horiz_commit(ctx);
+ ctx->y = pri_point->y;
+ }
+
+ if (seg == NULL) {
+ /* Insert new segment from input */
+ const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
+ art_svp_intersect_add_seg(ctx, in_seg);
+ if (ctx->in_curs < in->n_segs) {
+ const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
+ pri_point->x = next_seg->points[0].x;
+ pri_point->y = next_seg->points[0].y;
+ /* user_data is already NULL */
+ art_pri_insert(pq, pri_point);
+ } else
+ free(pri_point);
+ } else {
+ int n_stack = seg->n_stack;
+
+ if (n_stack > 1) {
+ art_svp_intersect_process_intersection(ctx, seg);
+ free(pri_point);
+ } else {
+ art_svp_intersect_advance_cursor(ctx, seg, pri_point);
+ }
+ }
+ }
+
+ art_svp_intersect_horiz_commit(ctx);
+
+ art_pri_free(pq);
+ free(ctx);
+}
+
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+typedef double artfloat;
+
+struct _ArtSVPRenderAAIter {
+ const ArtSVP *svp;
+ int x0, x1;
+ int y;
+ int seg_ix;
+
+ int *active_segs;
+ int n_active_segs;
+ int *cursor;
+ artfloat *seg_x;
+ artfloat *seg_dx;
+
+ ArtSVPRenderAAStep *steps;
+};
+
+static void art_svp_render_insert_active(int i, int *active_segs, int n_active_segs,
+ artfloat *seg_x, artfloat *seg_dx) {
+ int j;
+ artfloat x;
+ int tmp1, tmp2;
+
+ /* this is a cheap hack to get ^'s sorted correctly */
+ x = seg_x[i] + 0.001 * seg_dx[i];
+ for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
+
+ tmp1 = i;
+ while (j < n_active_segs) {
+ tmp2 = active_segs[j];
+ active_segs[j] = tmp1;
+ tmp1 = tmp2;
+ j++;
+ }
+ active_segs[j] = tmp1;
+}
+
+static void art_svp_render_delete_active(int *active_segs, int j, int n_active_segs) {
+ int k;
+
+ for (k = j; k < n_active_segs; k++)
+ active_segs[k] = active_segs[k + 1];
+}
+
+/* Render the sorted vector path in the given rectangle, antialiased.
+
+ This interface uses a callback for the actual pixel rendering. The
+ callback is called y1 - y0 times (once for each scan line). The y
+ coordinate is given as an argument for convenience (it could be
+ stored in the callback's private data and incremented on each
+ call).
+
+ The rendered polygon is represented in a semi-runlength format: a
+ start value and a sequence of "steps". Each step has an x
+ coordinate and a value delta. The resulting value at position x is
+ equal to the sum of the start value and all step delta values for
+ which the step x coordinate is less than or equal to x. An
+ efficient algorithm will traverse the steps left to right, keeping
+ a running sum.
+
+ All x coordinates in the steps are guaranteed to be x0 <= x < x1.
+ (This guarantee is a change from the gfonted vpaar renderer, and is
+ designed to simplify the callback).
+
+ There is now a further guarantee that no two steps will have the
+ same x value. This may allow for further speedup and simplification
+ of renderers.
+
+ The value 0x8000 represents 0% coverage by the polygon, while
+ 0xff8000 represents 100% coverage. This format is designed so that
+ >> 16 results in a standard 0x00..0xff value range, with nice
+ rounding.
+
+ Status of this routine:
+
+ Basic correctness: OK
+
+ Numerical stability: pretty good, although probably not
+ bulletproof.
+
+ Speed: Needs more aggressive culling of bounding boxes. Can
+ probably speed up the [x0,x1) clipping of step values. Can do more
+ of the step calculation in fixed point.
+
+ Precision: No known problems, although it should be tested
+ thoroughly, especially for symmetry.
+
+*/
+
+ArtSVPRenderAAIter *art_svp_render_aa_iter(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1) {
+ ArtSVPRenderAAIter *iter = art_new(ArtSVPRenderAAIter, 1);
+
+ iter->svp = svp;
+ iter->y = y0;
+ iter->x0 = x0;
+ iter->x1 = x1;
+ iter->seg_ix = 0;
+
+ iter->active_segs = art_new(int, svp->n_segs);
+ iter->cursor = art_new(int, svp->n_segs);
+ iter->seg_x = art_new(artfloat, svp->n_segs);
+ iter->seg_dx = art_new(artfloat, svp->n_segs);
+ iter->steps = art_new(ArtSVPRenderAAStep, x1 - x0);
+ iter->n_active_segs = 0;
+
+ return iter;
+}
+
+#define ADD_STEP(xpos, xdelta) \
+ /* stereotype code fragment for adding a step */ \
+ if (n_steps == 0 || steps[n_steps - 1].x < xpos) \
+ { \
+ sx = n_steps; \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ else \
+ { \
+ for (sx = n_steps; sx > 0; sx--) \
+ { \
+ if (steps[sx - 1].x == xpos) \
+ { \
+ steps[sx - 1].delta += xdelta; \
+ sx = n_steps; \
+ break; \
+ } \
+ else if (steps[sx - 1].x < xpos) \
+ { \
+ break; \
+ } \
+ } \
+ if (sx < n_steps) \
+ { \
+ memmove (&steps[sx + 1], &steps[sx], \
+ (n_steps - sx) * sizeof(steps[0])); \
+ steps[sx].x = xpos; \
+ steps[sx].delta = xdelta; \
+ n_steps++; \
+ } \
+ }
+
+void art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps) {
+ const ArtSVP *svp = iter->svp;
+ int *active_segs = iter->active_segs;
+ int n_active_segs = iter->n_active_segs;
+ int *cursor = iter->cursor;
+ artfloat *seg_x = iter->seg_x;
+ artfloat *seg_dx = iter->seg_dx;
+ int i = iter->seg_ix;
+ int j;
+ int x0 = iter->x0;
+ int x1 = iter->x1;
+ int y = iter->y;
+ int seg_index;
+
+ int x;
+ ArtSVPRenderAAStep *steps = iter->steps;
+ int n_steps;
+ artfloat y_top, y_bot;
+ artfloat x_top, x_bot;
+ artfloat x_min, x_max;
+ int ix_min, ix_max;
+ artfloat delta; /* delta should be int too? */
+ int last, this_;
+ int xdelta;
+ artfloat rslope, drslope;
+ int start;
+ const ArtSVPSeg *seg;
+ int curs;
+ artfloat dy;
+
+ int sx;
+
+ /* insert new active segments */
+ for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++) {
+ if (svp->segs[i].bbox.y1 > y &&
+ svp->segs[i].bbox.x0 < x1) {
+ seg = &svp->segs[i];
+ /* move cursor to topmost vector which overlaps [y,y+1) */
+ for (curs = 0; seg->points[curs + 1].y < y; curs++);
+ cursor[i] = curs;
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs(dy) >= EPSILON_6)
+ seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
+ dy;
+ else
+ seg_dx[i] = 1e12;
+ seg_x[i] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[i];
+ art_svp_render_insert_active(i, active_segs, n_active_segs++,
+ seg_x, seg_dx);
+ }
+ }
+
+ n_steps = 0;
+
+ /* render the runlengths, advancing and deleting as we go */
+ start = 0x8000;
+
+ for (j = 0; j < n_active_segs; j++) {
+ seg_index = active_segs[j];
+ seg = &svp->segs[seg_index];
+ curs = cursor[seg_index];
+ while (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1) {
+ y_top = y;
+ if (y_top < seg->points[curs].y)
+ y_top = seg->points[curs].y;
+ y_bot = y + 1;
+ if (y_bot > seg->points[curs + 1].y)
+ y_bot = seg->points[curs + 1].y;
+ if (y_top != y_bot) {
+ delta = (seg->dir ? 16711680.0 : -16711680.0) *
+ (y_bot - y_top);
+ x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
+ x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
+ if (x_top < x_bot) {
+ x_min = x_top;
+ x_max = x_bot;
+ } else {
+ x_min = x_bot;
+ x_max = x_top;
+ }
+ ix_min = (int)floor(x_min);
+ ix_max = (int)floor(x_max);
+ if (ix_min >= x1) {
+ /* skip; it starts to the right of the render region */
+ } else if (ix_max < x0)
+ /* it ends to the left of the render region */
+ start += (int)delta;
+ else if (ix_min == ix_max) {
+ /* case 1, antialias a single pixel */
+ xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
+
+ ADD_STEP(ix_min, xdelta)
+
+ if (ix_min + 1 < x1) {
+ xdelta = delta - xdelta;
+
+ ADD_STEP(ix_min + 1, xdelta)
+ }
+ } else {
+ /* case 2, antialias a run */
+ rslope = 1.0 / fabs(seg_dx[seg_index]);
+ drslope = delta * rslope;
+ last =
+ drslope * 0.5 *
+ (ix_min + 1 - x_min) * (ix_min + 1 - x_min);
+ xdelta = last;
+ if (ix_min >= x0) {
+ ADD_STEP(ix_min, xdelta)
+
+ x = ix_min + 1;
+ } else {
+ start += last;
+ x = x0;
+ }
+ if (ix_max > x1)
+ ix_max = x1;
+ for (; x < ix_max; x++) {
+ this_ = (seg->dir ? 16711680.0 : -16711680.0) * rslope *
+ (x + 0.5 - x_min);
+ xdelta = this_ - last;
+ last = this_;
+
+ ADD_STEP(x, xdelta)
+ }
+ if (x < x1) {
+ this_ =
+ delta * (1 - 0.5 *
+ (x_max - ix_max) * (x_max - ix_max) *
+ rslope);
+ xdelta = this_ - last;
+ last = this_;
+
+ ADD_STEP(x, xdelta)
+
+ if (x + 1 < x1) {
+ xdelta = delta - last;
+
+ ADD_STEP(x + 1, xdelta)
+ }
+ }
+ }
+ }
+ curs++;
+ if (curs != seg->n_points - 1 &&
+ seg->points[curs].y < y + 1) {
+ dy = seg->points[curs + 1].y - seg->points[curs].y;
+ if (fabs(dy) >= EPSILON_6)
+ seg_dx[seg_index] = (seg->points[curs + 1].x -
+ seg->points[curs].x) / dy;
+ else
+ seg_dx[seg_index] = 1e12;
+ seg_x[seg_index] = seg->points[curs].x +
+ (y - seg->points[curs].y) * seg_dx[seg_index];
+ }
+ /* break here, instead of duplicating predicate in while? */
+ }
+ if (seg->points[curs].y >= y + 1) {
+ curs--;
+ cursor[seg_index] = curs;
+ seg_x[seg_index] += seg_dx[seg_index];
+ } else {
+ art_svp_render_delete_active(active_segs, j--,
+ --n_active_segs);
+ }
+ }
+
+ *p_start = start;
+ *p_steps = steps;
+ *p_n_steps = n_steps;
+
+ iter->seg_ix = i;
+ iter->n_active_segs = n_active_segs;
+ iter->y++;
+}
+
+void art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter) {
+ free(iter->steps);
+
+ free(iter->seg_dx);
+ free(iter->seg_x);
+ free(iter->cursor);
+ free(iter->active_segs);
+ free(iter);
+}
+
+/**
+ * art_svp_render_aa: Render SVP antialiased.
+ * @svp: The #ArtSVP to render.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @callback: The callback which actually paints the pixels.
+ * @callback_data: Private data for @callback.
+ *
+ * Renders the sorted vector path in the given rectangle, antialiased.
+ *
+ * This interface uses a callback for the actual pixel rendering. The
+ * callback is called @y1 - @y0 times (once for each scan line). The y
+ * coordinate is given as an argument for convenience (it could be
+ * stored in the callback's private data and incremented on each
+ * call).
+ *
+ * The rendered polygon is represented in a semi-runlength format: a
+ * start value and a sequence of "steps". Each step has an x
+ * coordinate and a value delta. The resulting value at position x is
+ * equal to the sum of the start value and all step delta values for
+ * which the step x coordinate is less than or equal to x. An
+ * efficient algorithm will traverse the steps left to right, keeping
+ * a running sum.
+ *
+ * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
+ * (This guarantee is a change from the gfonted vpaar renderer from
+ * which this routine is derived, and is designed to simplify the
+ * callback).
+ *
+ * The value 0x8000 represents 0% coverage by the polygon, while
+ * 0xff8000 represents 100% coverage. This format is designed so that
+ * >> 16 results in a standard 0x00..0xff value range, with nice
+ * rounding.
+ *
+ **/
+void art_svp_render_aa(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback)(void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data) {
+ ArtSVPRenderAAIter *iter;
+ int y;
+ int start;
+ ArtSVPRenderAAStep *steps;
+ int n_steps;
+
+ iter = art_svp_render_aa_iter(svp, x0, y0, x1, y1);
+
+
+ for (y = y0; y < y1; y++) {
+ art_svp_render_aa_iter_step(iter, &start, &steps, &n_steps);
+ (*callback)(callback_data, y, start, steps, n_steps);
+ }
+
+ art_svp_render_aa_iter_done(iter);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/art.h b/engines/sword25/gfx/image/art.h
new file mode 100644
index 0000000000..90baa770cf
--- /dev/null
+++ b/engines/sword25/gfx/image/art.h
@@ -0,0 +1,279 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/* Simple macros to set up storage allocation and basic types for libart
+ functions. */
+
+#ifndef __ART_H__
+#define __ART_H__
+
+#include "common/scummsys.h"
+
+namespace Sword25 {
+
+typedef byte art_u8;
+typedef uint16 art_u16;
+typedef uint32 art_u32;
+
+/* These aren't, strictly speaking, configuration macros, but they're
+ damn handy to have around, and may be worth playing with for
+ debugging. */
+#define art_new(type, n) ((type *)malloc ((n) * sizeof(type)))
+
+#define art_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
+
+/* This one must be used carefully - in particular, p and max should
+ be variables. They can also be pstruct->el lvalues. */
+#define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0)
+
+typedef int art_boolean;
+#define ART_FALSE 0
+#define ART_TRUE 1
+
+/* define pi */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
+#endif /* M_SQRT2 */
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
+#define ART_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define ART_GNUC_NORETURN \
+ __attribute__((__noreturn__))
+#else /* !__GNUC__ */
+#define ART_GNUC_PRINTF( format_idx, arg_idx )
+#define ART_GNUC_NORETURN
+#endif /* !__GNUC__ */
+
+void ART_GNUC_NORETURN
+art_die(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+void
+art_warn(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+typedef struct _ArtDRect ArtDRect;
+typedef struct _ArtIRect ArtIRect;
+
+struct _ArtDRect {
+ /*< public >*/
+ double x0, y0, x1, y1;
+};
+
+struct _ArtIRect {
+ /*< public >*/
+ int x0, y0, x1, y1;
+};
+
+typedef struct _ArtPoint ArtPoint;
+
+struct _ArtPoint {
+ /*< public >*/
+ double x, y;
+};
+
+/* Basic data structures and constructors for sorted vector paths */
+
+typedef struct _ArtSVP ArtSVP;
+typedef struct _ArtSVPSeg ArtSVPSeg;
+
+struct _ArtSVPSeg {
+ int n_points;
+ int dir; /* == 0 for "up", 1 for "down" */
+ ArtDRect bbox;
+ ArtPoint *points;
+};
+
+struct _ArtSVP {
+ int n_segs;
+ ArtSVPSeg segs[1];
+};
+
+void
+art_svp_free(ArtSVP *svp);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+/* Basic data structures and constructors for bezier paths */
+
+typedef enum {
+ ART_MOVETO,
+ ART_MOVETO_OPEN,
+ ART_CURVETO,
+ ART_LINETO,
+ ART_END
+} ArtPathcode;
+
+typedef struct _ArtBpath ArtBpath;
+
+struct _ArtBpath {
+ /*< public >*/
+ ArtPathcode code;
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+ double x3;
+ double y3;
+};
+
+/* Basic data structures and constructors for simple vector paths */
+
+typedef struct _ArtVpath ArtVpath;
+
+/* CURVETO is not allowed! */
+struct _ArtVpath {
+ ArtPathcode code;
+ double x;
+ double y;
+};
+
+/* Some of the functions need to go into their own modules */
+
+void
+art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+ ArtPathcode code, double x, double y);
+
+ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness);
+
+/* The funky new SVP intersector. */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+ ART_WIND_RULE_NONZERO,
+ ART_WIND_RULE_INTERSECT,
+ ART_WIND_RULE_ODDEVEN,
+ ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+typedef struct _ArtSvpWriter ArtSvpWriter;
+
+struct _ArtSvpWriter {
+ int (*add_segment)(ArtSvpWriter *self, int wind_left, int delta_wind,
+ double x, double y);
+ void (*add_point)(ArtSvpWriter *self, int seg_id, double x, double y);
+ void (*close_segment)(ArtSvpWriter *self, int seg_id);
+};
+
+ArtSvpWriter *
+art_svp_writer_rewind_new(ArtWindRule rule);
+
+ArtSVP *
+art_svp_writer_rewind_reap(ArtSvpWriter *self);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+void
+art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out);
+
+
+/* Sort vector paths into sorted vector paths. */
+
+ArtSVP *
+art_svp_from_vpath(ArtVpath *vpath);
+
+/* Sort vector paths into sorted vector paths. */
+
+typedef enum {
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_JOIN_ROUND,
+ ART_PATH_STROKE_JOIN_BEVEL
+} ArtPathStrokeJoinType;
+
+typedef enum {
+ ART_PATH_STROKE_CAP_BUTT,
+ ART_PATH_STROKE_CAP_ROUND,
+ ART_PATH_STROKE_CAP_SQUARE
+} ArtPathStrokeCapType;
+
+ArtSVP *
+art_svp_vpath_stroke(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+/* This version may have winding numbers exceeding 1. */
+ArtVpath *
+art_svp_vpath_stroke_raw(ArtVpath *vpath,
+ ArtPathStrokeJoinType join,
+ ArtPathStrokeCapType cap,
+ double line_width,
+ double miter_limit,
+ double flatness);
+
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
+typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter;
+
+struct _ArtSVPRenderAAStep {
+ int x;
+ int delta; /* stored with 16 fractional bits */
+};
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1);
+
+void
+art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+ ArtSVPRenderAAStep **p_steps, int *p_n_steps);
+
+void
+art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter);
+
+void
+art_svp_render_aa(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ void (*callback)(void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps),
+ void *callback_data);
+
+} // End of namespace Sword25
+
+#endif /* __ART_H__ */
diff --git a/engines/sword25/gfx/image/b25sloader.cpp b/engines/sword25/gfx/image/b25sloader.cpp
new file mode 100644
index 0000000000..513e74ccea
--- /dev/null
+++ b/engines/sword25/gfx/image/b25sloader.cpp
@@ -0,0 +1,119 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/image/b25sloader.h"
+#include "sword25/gfx/image/pngloader.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "B25SLOADER"
+
+// -----------------------------------------------------------------------------
+
+namespace {
+static Common::String LoadString(Common::ReadStream &In, uint MaxSize = 999) {
+ Common::String Result;
+
+ char ch = (char)In.readByte();
+ while ((ch != '\0') && (ch != ' ')) {
+ Result += ch;
+ if (Result.size() >= MaxSize) break;
+ ch = (char)In.readByte();
+ }
+
+ return Result;
+}
+
+uint FindEmbeddedPNG(const byte *FileDataPtr, uint FileSize) {
+ if (memcmp(FileDataPtr, "BS25SAVEGAME", 12))
+ return 0;
+
+ // Read in the header
+ Common::MemoryReadStream stream(FileDataPtr, FileSize);
+
+ // Headerinformationen der Spielstandes einlesen.
+ uint compressedGamedataSize;
+ LoadString(stream);
+ LoadString(stream);
+ Common::String gameSize = LoadString(stream);
+ compressedGamedataSize = atoi(gameSize.c_str());
+ LoadString(stream);
+
+ // Return the offset of where the thumbnail starts
+ return static_cast<uint>(stream.pos() + compressedGamedataSize);
+}
+}
+
+// -----------------------------------------------------------------------------
+
+bool B25SLoader::IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0) {
+ return PNGLoader::DoIsCorrectImageFormat(FileDataPtr + PNGOffset, FileSize - PNGOffset);
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool B25SLoader::DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
+ int &Width, int &Height, int &Pitch) {
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0) {
+ return PNGLoader::DoDecodeImage(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool B25SLoader::ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
+ // PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.
+ uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);
+ if (PNGOffset > 0) {
+ return PNGLoader::DoImageProperties(FileDataPtr + PNGOffset, FileSize - PNGOffset, ColorFormat, Width, Height);
+ }
+
+ return false;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/b25sloader.h b/engines/sword25/gfx/image/b25sloader.h
new file mode 100644
index 0000000000..fbfaf87194
--- /dev/null
+++ b/engines/sword25/gfx/image/b25sloader.h
@@ -0,0 +1,67 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_B25SLOADER_H
+#define SWORD25_B25SLOADER_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/imageloader.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class B25SLoader : public ImageLoader {
+public:
+ static ImageLoader *CreateInstance() {
+ return static_cast<ImageLoader *>(new B25SLoader());
+ }
+
+protected:
+ virtual bool IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
+ virtual bool DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
+ int &Width, int &Height, int &Pitch);
+ virtual bool ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
+
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/image.h b/engines/sword25/gfx/image/image.h
new file mode 100644
index 0000000000..bc0ff20d77
--- /dev/null
+++ b/engines/sword25/gfx/image/image.h
@@ -0,0 +1,222 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_Image
+ --------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_IMAGE_H
+#define SWORD25_IMAGE_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "common/rect.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+class Image {
+public:
+ virtual ~Image() {};
+
+ // Enums
+ /**
+ @brief Die möglichen Flippingparameter für die Blit-Methode.
+ */
+ enum FLIP_FLAGS {
+ /// Das Bild wird nicht gespiegelt.
+ FLIP_NONE = 0,
+ /// Das Bild wird an der horizontalen Achse gespiegelt.
+ FLIP_H = 1,
+ /// Das Bild wird an der vertikalen Achse gespiegelt.
+ FLIP_V = 2,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_HV = FLIP_H | FLIP_V,
+ /// Das Bild wird an der horizontalen und vertikalen Achse gespiegelt.
+ FLIP_VH = FLIP_H | FLIP_V
+ };
+
+ //@{
+ /** @name Accessor-Methoden */
+
+ /**
+ @brief Gibt die Breite des Bildes in Pixeln zurück
+ */
+ virtual int getWidth() const = 0;
+
+ /**
+ @brief Gibt die Höhe des Bildes in Pixeln zurück
+ */
+ virtual int getHeight() const = 0;
+
+ /**
+ @brief Gibt das Farbformat des Bildes zurück
+ */
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const = 0;
+
+ //@}
+
+ //@{
+ /** @name Render-Methoden */
+
+ /**
+ @brief Rendert das Bild in den Framebuffer.
+ @param pDest ein Pointer auf das Zielbild. In den meisten Fällen ist dies der Framebuffer.
+ @param PosX die Position auf der X-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param PosY die Position auf der Y-Achse im Zielbild in Pixeln, an der das Bild gerendert werden soll.<br>
+ Der Standardwert ist 0.
+ @param Flipping gibt an, wie das Bild gespiegelt werden soll.<br>
+ Der Standardwert ist BS_Image::FLIP_NONE (keine Spiegelung)
+ @param pSrcPartRect Pointer auf ein Common::Rect, welches den Ausschnitt des Quellbildes spezifiziert, der gerendert
+ werden soll oder NULL, falls das gesamte Bild gerendert werden soll.<br>
+ Dieser Ausschnitt bezieht sich auf das ungespiegelte und unskalierte Bild.<br>
+ Der Standardwert ist NULL.
+ @param Color ein ARGB Farbwert, der die Parameter für die Farbmodulation und fürs Alphablending festlegt.<br>
+ Die Alpha-Komponente der Farbe bestimmt den Alphablending Parameter (0 = keine Deckung, 255 = volle Deckung).<br>
+ Die Farbkomponenten geben die Farbe für die Farbmodulation an.<br>
+ Der Standardwert is BS_ARGB(255, 255, 255, 255) (volle Deckung, keine Farbmodulation).
+ Zum Erzeugen des Farbwertes können die Makros BS_RGB und BS_ARGB benutzt werden.
+ @param Width gibt die Ausgabebreite des Bildausschnittes an.
+ Falls diese von der Breite des Bildausschnittes abweicht wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @param Width gibt die Ausgabehöhe des Bildausschnittes an.
+ Falls diese von der Höhe des Bildauschnittes abweicht, wird
+ das Bild entsprechend Skaliert.<br>
+ Der Wert -1 gibt an, dass das Bild nicht Skaliert werden soll.<br>
+ Der Standardwert ist -1.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark Er werden nicht alle Blitting-Operationen von allen BS_Image-Klassen unterstützt.<br>
+ Mehr Informationen gibt es in der Klassenbeschreibung von BS_Image und durch folgende Methoden:
+ - IsBlitTarget()
+ - IsScalingAllowed()
+ - IsFillingAllowed()
+ - IsAlphaAllowed()
+ - IsColorModulationAllowed()
+ - IsSetContentAllowed()
+ */
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1) = 0;
+
+ /**
+ @brief Füllt einen Rechteckigen Bereich des Bildes mit einer Farbe.
+ @param pFillRect Pointer auf ein Common::Rect, welches den Ausschnitt des Bildes spezifiziert, der gefüllt
+ werden soll oder NULL, falls das gesamte Bild gefüllt werden soll.<br>
+ Der Standardwert ist NULL.
+ @param Color der 32 Bit Farbwert mit dem der Bildbereich gefüllt werden soll.
+ @remark Es ist möglich über die Methode transparente Rechtecke darzustellen, indem man eine Farbe mit einem Alphawert ungleich
+ 255 angibt.
+ @remark Unabhängig vom Farbformat des Bildes muss ein 32 Bit Farbwert angegeben werden. Zur Erzeugung, können die Makros
+ BS_RGB und BS_ARGB benutzt werden.
+ @remark Falls das Rechteck nicht völlig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
+ */
+ virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0)) = 0;
+
+ /**
+ @brief Füllt den Inhalt des Bildes mit Pixeldaten.
+ @param Pixeldata ein Vector der die Pixeldaten enthält. Sie müssen in dem Farbformat des Bildes vorliegen und es müssen genügend Daten
+ vorhanden sein, um das ganze Bild zu füllen.
+ @param Offset der Offset in Byte im Pixeldata-Vector an dem sich der erste zu schreibende Pixel befindet.<br>
+ Der Standardwert ist 0.
+ @param Stride der Abstand in Byte zwischen dem Zeilenende und dem Beginn einer neuen Zeile im Pixeldata-Vector.<br>
+ Der Standardwert ist 0.
+ @return Gibt false zurück, falls der Aufruf fehlgeschlagen ist.
+ @remark Ein Aufruf dieser Methode ist nur erlaubt, wenn IsSetContentAllowed() true zurückgibt.
+ */
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride) = 0;
+
+ /**
+ @brief Liest einen Pixel des Bildes.
+ @param X die X-Koordinate des Pixels.
+ @param Y die Y-Koordinate des Pixels
+ @return Gibt den 32-Bit Farbwert des Pixels an der übergebenen Koordinate zurück.
+ @remark Diese Methode sollte auf keine Fall benutzt werden um größere Teile des Bildes zu lesen, da sie sehr langsam ist. Sie ist
+ eher dafür gedacht einzelne Pixel des Bildes auszulesen.
+ */
+ virtual uint getPixel(int x, int y) = 0;
+
+ //@{
+ /** @name Auskunfts-Methoden */
+
+ /**
+ @brief Überprüft, ob an dem BS_Image Blit() aufgerufen werden darf.
+ @return Gibt false zurück, falls ein Blit()-Aufruf an diesem Objekt nicht gestattet ist.
+ */
+ virtual bool isBlitSource() const = 0;
+
+ /**
+ @brief Überprüft, ob das BS_Image ein Zielbild für einen Blit-Aufruf sein kann.
+ @return Gibt false zurück, falls ein Blit-Aufruf mit diesem Objekt als Ziel nicht gestattet ist.
+ */
+ virtual bool isBlitTarget() const = 0;
+
+ /**
+ @brief Gibt true zurück, falls das BS_Image bei einem Aufruf von Blit() skaliert dargestellt werden kann.
+ */
+ virtual bool isScalingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image mit einem Aufruf von Fill() gefüllt werden kann.
+ */
+ virtual bool isFillingAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit einem Alphawert dargestellt werden kann.
+ */
+ virtual bool isAlphaAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn das BS_Image bei einem Aufruf von Blit() mit Farbmodulation dargestellt werden kann.
+ */
+ virtual bool isColorModulationAllowed() const = 0;
+
+ /**
+ @brief Gibt true zurück, wenn der Inhalt des BS_Image durch eine Aufruf von SetContent() ausgetauscht werden kann.
+ */
+ virtual bool isSetContentAllowed() const = 0;
+
+ //@}
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/imageloader.cpp b/engines/sword25/gfx/image/imageloader.cpp
new file mode 100644
index 0000000000..55ce833cc9
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader.cpp
@@ -0,0 +1,129 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/image/imageloader_ids.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "IMAGELOADER"
+
+// Statische Elemente der Klasse BS_ImageLoader intialisieren.
+Common::List<ImageLoader *> ImageLoader::_ImageLoaderList;
+bool ImageLoader::_ImageLoaderListInitialized = false;
+
+// Lade Methode
+// ------------
+
+bool ImageLoader::LoadImage(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS ColorFormat,
+ byte *&pUncompressedData,
+ int &Width, int &Height,
+ int &Pitch) {
+ // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
+ if (!_ImageLoaderListInitialized)
+ _InitializeLoaderList();
+
+ // Passenden BS_ImageLoader finden und Bild dekodieren
+ ImageLoader *pLoader = _FindSuitableImageLoader(pFileData, FileSize);
+ if (pLoader) {
+ return pLoader->DecodeImage(pFileData, FileSize,
+ ColorFormat,
+ pUncompressedData,
+ Width, Height,
+ Pitch);
+ }
+
+ return false;
+}
+
+// Info Methode
+// ------------
+
+bool ImageLoader::ExtractImageProperties(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS &ColorFormat,
+ int &Width, int &Height) {
+ // Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.
+ if (!_ImageLoaderListInitialized)
+ _InitializeLoaderList();
+
+ // Passenden BS_ImageLoader finden und Bildeigenschaften auslesen.
+ ImageLoader *pLoader = _FindSuitableImageLoader(pFileData, FileSize);
+ if (pLoader) {
+ return pLoader->ImageProperties(pFileData, FileSize,
+ ColorFormat,
+ Width, Height);
+ }
+
+ return false;
+}
+
+// Verwaltungs Methoden
+// --------------------
+
+void ImageLoader::_InitializeLoaderList() {
+ // Von jedem BS_ImageLoader wird eine Instanz erzeugt, diese fügen sich selbständig in die BS_ImageLoader-Liste ein.
+ for (int i = 0; i < BS_IMAGELOADER_COUNT; i++)
+ BS_IMAGELOADER_IDS[i]();
+
+ // Die Liste als gefüllt markieren.
+ _ImageLoaderListInitialized = true;
+
+ // Sicherstellen, dass beim Beenden alle BS_ImageLoader Instanzen zerstört werden.
+ atexit(ImageLoader::_DeinitializeLoaderList);
+}
+
+void ImageLoader::_DeinitializeLoaderList() {
+ while (!_ImageLoaderList.empty()) {
+ delete _ImageLoaderList.back();
+ _ImageLoaderList.pop_back();
+ }
+}
+
+ImageLoader *ImageLoader::_FindSuitableImageLoader(const byte *pFileData, uint FileSize) {
+ // Alle BS_ImageLoader-Objekte durchgehen, bis eins gefunden wurde, dass das Bild laden kann
+ Common::List<ImageLoader *>::iterator Iter = _ImageLoaderList.begin();
+ for (; Iter != _ImageLoaderList.end(); ++Iter) {
+ // Falls ein geeigneter BS-ImageLoader gefunden wurde, wird er zurückgegeben.
+ if ((*Iter)->IsCorrectImageFormat(pFileData, FileSize)) {
+ return (*Iter);
+ }
+ }
+
+ // Es konnte kein passender BS_ImageLoader gefunden werden.
+ BS_LOG_ERRORLN("Could not find suitable image loader for image data.");
+ return NULL;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/imageloader.h b/engines/sword25/gfx/image/imageloader.h
new file mode 100644
index 0000000000..aae48a083c
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader.h
@@ -0,0 +1,209 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_ImageLoader
+ --------------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_IMAGELOADER_H
+#define SWORD25_IMAGELOADER_H
+
+// Includes
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+/**
+ @brief Über die statischen Methoden dieser Klasse werden alle unterstützten Bildformate geladen.
+
+ Zum Laden von Bildern wird die #LoadImage-Methode benutzt.
+
+ Außerdem stellt diese Klasse das Interface da, das alle Klassen implementieren müssen, die Bildformate einlesen.<br>
+ Zur Unterstützung eines neuen Bildformates muss folgendermaßen vorgegangen werden:
+ - Erzeugen einer neuen von #BS_ImageLoader abgeleiteten Klasse, die die Methoden #IsCorrectImageFormat und #DecodeImage impelementiert.
+ - Die Klasse muss eine statische Methode haben, die eine Instanz von ihr erzeugt und einen Pointer darauf zurückgibt.
+ - Diese Methode muss in der Liste in der Datei imageloader_ids.h eingetragen werden.
+ - Die Klasse muss JEDES Eingabebild seines Bildformates in die folgenden Farbformate konvertieren können:
+ - BS_GraphicEngine::CF_ARGB32
+ - Zum Konvertieren der Bilddaten können die Hilfsmethoden dieser Klasse benutzt werden, die ARGB Bilddaten in alle benötigten
+ Farbformate konvertieren.
+*/
+class ImageLoader {
+public:
+
+ //@{
+ /** @name Lade Methoden */
+
+ /**
+ @brief Lädt eine Bilddatei.
+
+ Diese Methode kann sämtliche unterstütztem Bildformate lesen. Die Methode erkennt selbstständing um welches Dateiformat es sich
+ bei der vorliegenden Datei handelt.<br>
+ Bisher wird nur PNG unterstützt.
+
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe der Bilddaten in Byte.
+ @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
+ Folgende Farbformate werden unterstützt:
+ - BS_GraphicEngine::CF_ARGB32
+ @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
+ @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
+ @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
+ @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
+ @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
+ @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ */
+ static bool LoadImage(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS ColorFormat,
+ byte *&pUncompressedData,
+ int &Width, int &Height,
+ int &Pitch);
+
+ /**
+ @brief Liest die Bildeigenschaften eines Bildes aus.
+
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe des Bilddaten in Byte.
+ @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
+ @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
+ @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
+ @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ */
+ static bool ExtractImageProperties(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS &ColorFormat,
+ int &Width, int &Height);
+ //@}
+
+protected:
+
+ // Protected Konstruktor, damit Instanzen dieser Klasse nur von BS_ImageLoader-Objekten erstellt werden können
+ /**
+ @brief Der Standardkonstruktor.
+
+ Dieser Konstruktor registriert alle Instanzen von #BS_ImageLoader-Klassen in einer Liste.<br>
+ Diese Liste enthält jeweils eine Instanz jedes #BS_ImageLoader und wird benutzt um beliebige Bilddateien einem Loader zuzuordnen.
+ @remark Dieser Konstruktor ist protected damit nur #BS_ImageLoader-Objekte diese Klasse instanziieren können.
+ */
+ ImageLoader() {
+ // Klasse registrieren
+ _ImageLoaderList.push_front(this);
+ }
+
+ virtual ~ImageLoader() {}
+
+ //@{
+ /** @name Abstrakte Methoden */
+
+ /**
+ @brief Gibt an, ob der #BS_ImageLoader ein Bild lesen kann.
+ @param pFileData ein Pointer auf die kompletten Daten des Bildes.
+ @param FileSize die Größe der Daten in Byte.
+ @return Gibt true zurück, wenn der #BS_ImageLoader das Bild lesen kann, ansonsten false.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool IsCorrectImageFormat(const byte *pFileData, uint FileSize) = 0;
+
+ /**
+ @brief Lädt eine Bilddatei.
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe der Bilddaten in Byte.
+ @param ColorFormat gibt das gewünschte Farbformat an, in das die Bilddaten konvertiert werden sollen.<br>
+ Folgende Farbformate werden unterstützt:
+ - BS_GraphicEngine::CF_ARGB32
+ @param pUncompressedData nach erfolgreichen Laden zeigt dieser Pointer auf die enpackten und konvertierten Bilddaten.
+ @param Width gibt nach erfolgreichen Laden die Breite des geladenen Bildes an.
+ @param Height gibt nach erfolgreichen Laden die Höhe des geladenen Bildes an.
+ @param Pitch gibt nach erfolgreichen Laden die Länge einer Bildzeile in Byte an.
+ @return Gibt false zurück, falls das Laden fehlgeschlagen ist.
+ @remark Die Größe der Ausgabedaten in Bytes kann wie folgt berechnet werden: Pitch * Height.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool DecodeImage(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS ColorFormat,
+ byte *&pUncompressedData,
+ int &Width, int &Height,
+ int &Pitch) = 0;
+
+ /**
+ @brief Liest die Bildeigenschaften aus.
+ @param pFileData ein Pointer auf die Bilddaten.
+ @param FileSize die Größe des Bilddaten in Byte.
+ @param ColorFormat enthält nach einem erfolgreichem Aufruf das Farbformat des Bildes.
+ @param Width enthält nach einem erfolgreichem Aufruf die Breite des Bildes in Pixeln.
+ @param Height enthält nach einem erfolgreichem Aufruf die Höhe des Bildes in Pixeln.
+ @return Gibt false zurück, wenn die Bildeigenschaften nicht ausgelesen werden konnten.
+ @remark Es darf nicht vergessen werden, die Ausgabedaten nach erfolgter Benutzung mit delete freizugeben.
+ @remark Diese Methode muss von allen BS_ImageLoader Klassen implementiert werden.
+ */
+ virtual bool ImageProperties(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS &ColorFormat,
+ int &Width, int &Height) = 0;
+
+ //@}
+
+private:
+
+ /**
+ @brief Erzeugt je eine Instanz aller BS_ImageLoader Klassen und fügt diese in eine interne Liste ein. Diese werden dann beim
+ Laden von Bildern benutzt.
+ @remark Die Klassen müssen in der Datei imageloader_ids.h eingetragen sein, damit sie an dieser Stelle berücksichtigt werden.
+ */
+ static void _InitializeLoaderList();
+
+ /**
+ @brief Zerstört alle Instanzen von BS_ImageLoader Klassen, die in dieser Klasse registriert sind.
+ */
+ static void _DeinitializeLoaderList();
+
+ /**
+ @brief Sucht zu Bilddaten ein BS_ImageLoader Objekt, dass die Bilddaten dekodieren kann.
+ @return Gibt einen Pointer auf ein passendes BS_ImageLoader Objekt zurück, oder NULL, wenn kein passendes Objekt gefunden wurde.
+ */
+ static ImageLoader *_FindSuitableImageLoader(const byte *pFileData, uint FileSize);
+
+ static Common::List<ImageLoader *> _ImageLoaderList; // Die Liste aller BS_ImageLoader-Objekte
+ static bool _ImageLoaderListInitialized; // Gibt an, ob die Liste schon intialisiert wurde
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/imageloader_ids.h b/engines/sword25/gfx/image/imageloader_ids.h
new file mode 100644
index 0000000000..8fb6872ad7
--- /dev/null
+++ b/engines/sword25/gfx/image/imageloader_ids.h
@@ -0,0 +1,62 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ imageloader_ids.h
+ -----------------
+ In dieser Datei sind alle ImageLoader verzeichnet.
+ JEDER neuer ImageLoader muss hier eingetragen werden, ansonsten wird er beim Laden eines Bildes nicht berücksichtigt.
+
+ Autor: Malte Thiesen
+*/
+
+#include "sword25/gfx/image/imageloader.h"
+
+// Die Headerdateien der ImageLoader müssen hier eingebunden werden
+#include "sword25/gfx/image/pngloader.h"
+#include "sword25/gfx/image/b25sloader.h"
+
+namespace Sword25 {
+
+// Die Tabelle enthält Pointer auf statische Member-Funktionen innerhalb der Klassen, die eine Instanz der Klasse
+// erzeugen
+typedef ImageLoader*(*BS_IMAGELOADER_NEW)();
+const BS_IMAGELOADER_NEW BS_IMAGELOADER_IDS[] = {
+ PNGLoader::CreateInstance,
+ B25SLoader::CreateInstance,
+};
+const int BS_IMAGELOADER_COUNT = sizeof(BS_IMAGELOADER_IDS) / sizeof(BS_IMAGELOADER_NEW);
+
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp
new file mode 100644
index 0000000000..c59d68724d
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.cpp
@@ -0,0 +1,283 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/image/pngloader.h"
+#include <png.h>
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "PNGLOADER"
+
+// -----------------------------------------------------------------------------
+// Konstruktor / Destruktor
+// -----------------------------------------------------------------------------
+
+PNGLoader::PNGLoader() {
+}
+
+// -----------------------------------------------------------------------------
+// Laden
+// -----------------------------------------------------------------------------
+
+static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
+ memcpy(data, (char *)png_ptr->io_ptr, length);
+ png_ptr->io_ptr = (void *)((png_size_t)png_ptr->io_ptr + length);
+}
+
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
+ int &Width, int &Height, int &Pitch) {
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ png_bytep RawDataBuffer = NULL;
+ png_bytep *pRowPtr = NULL;
+
+ int BitDepth;
+ int ColorType;
+ int InterlaceType;
+ int i;
+
+ // Zielfarbformat überprüfen
+ if (ColorFormat != GraphicEngine::CF_ARGB32) {
+ BS_LOG_ERRORLN("Illegal or unsupported color format.");
+ return false;
+ }
+
+ // PNG Signatur überprüfen
+ if (!png_check_sig(reinterpret_cast<png_bytep>(const_cast<byte *>(FileDataPtr)), 8)) {
+ error("png_check_sig failed");
+ }
+
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ error("Could not create libpng read struct.");
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ error("Could not create libpng info struct.");
+ }
+
+ // Alternative Lesefunktion benutzen
+ png_set_read_fn(png_ptr, (void *)FileDataPtr, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, &InterlaceType, NULL, NULL);
+
+ // Pitch des Ausgabebildes berechnen
+ Pitch = GraphicEngine::CalcPitch(ColorFormat, Width);
+
+ // Speicher für die endgültigen Bilddaten reservieren
+ // Dieses geschieht vor dem reservieren von Speicher für temporäre Bilddaten um die Fragmentierung des Speichers gering zu halten
+ UncompressedDataPtr = new byte[Pitch * Height];
+ if (!UncompressedDataPtr) {
+ error("Could not allocate memory for output image.");
+ }
+
+ // Bilder jeglicher Farbformate werden zunächst in ARGB Bilder umgewandelt
+ if (BitDepth == 16)
+ png_set_strip_16(png_ptr);
+ if (ColorType == PNG_COLOR_TYPE_PALETTE)
+ png_set_expand(png_ptr);
+ if (BitDepth < 8)
+ png_set_expand(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+ if (ColorType == PNG_COLOR_TYPE_GRAY ||
+ ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ png_set_bgr(png_ptr);
+
+ if (ColorType != PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ // Nachdem die Transformationen registriert wurden, werden die Bilddaten erneut eingelesen
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
+
+ // PNGs ohne Interlacing werden Zeilenweise eingelesen
+ if (InterlaceType == PNG_INTERLACE_NONE) {
+ // Speicher für eine Bildzeile reservieren
+ RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
+ if (!RawDataBuffer) {
+ error("Could not allocate memory for row buffer.");
+ }
+
+ // Bilddaten zeilenweise einlesen und in das gewünschte Zielformat konvertieren
+ for (i = 0; i < Height; i++) {
+ // Zeile einlesen
+ png_read_row(png_ptr, RawDataBuffer, NULL);
+
+ // Zeile konvertieren
+ switch (ColorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ memcpy(&UncompressedDataPtr[i * Pitch], RawDataBuffer, Pitch);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ } else {
+ // PNGs mit Interlacing werden an einem Stück eingelesen
+ // Speicher für das komplette Bild reservieren
+ RawDataBuffer = new png_byte[png_get_rowbytes(png_ptr, info_ptr) * Height];
+ if (!RawDataBuffer) {
+ error("Could not allocate memory for raw image buffer.");
+ }
+
+ // Speicher für die Rowpointer reservieren
+ pRowPtr = new png_bytep[Height];
+ if (!pRowPtr) {
+ error("Could not allocate memory for row pointers.");
+ }
+
+ // Alle Rowpointer mit den richtigen Offsets initialisieren
+ for (i = 0; i < Height; i++)
+ pRowPtr[i] = RawDataBuffer + i * png_get_rowbytes(png_ptr, info_ptr);
+
+ // Bild einlesen
+ png_read_image(png_ptr, pRowPtr);
+
+ // Bilddaten zeilenweise in das gewünschte Ausgabeformat konvertieren
+ switch (ColorFormat) {
+ case GraphicEngine::CF_ARGB32:
+ for (i = 0; i < Height; i++)
+ memcpy(&UncompressedDataPtr[i * Pitch], &RawDataBuffer[i * png_get_rowbytes(png_ptr, info_ptr)], Pitch);
+ break;
+ default:
+ error("Unhandled case in DoDecodeImage");
+ break;
+ }
+ }
+
+ // Die zusätzlichen Daten am Ende des Bildes lesen
+ png_read_end(png_ptr, NULL);
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ // Temporäre Buffer freigeben
+ delete[] pRowPtr;
+ delete[] RawDataBuffer;
+
+ // Der Funktionsaufruf war erfolgreich
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
+ int &Width, int &Height, int &Pitch) {
+ return DoDecodeImage(FileDataPtr, FileSize, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);
+}
+
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::DoImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
+ // PNG Signatur überprüfen
+ if (!DoIsCorrectImageFormat(FileDataPtr, FileSize)) return false;
+
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ // Die beiden PNG Strukturen erstellen
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ error("Could not create libpng read struct.");
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ error("Could not create libpng info struct.");
+ }
+
+ // Alternative Lesefunktion benutzen
+ png_set_read_fn(png_ptr, (void *)FileDataPtr, png_user_read_data);
+
+ // PNG Header einlesen
+ png_read_info(png_ptr, info_ptr);
+
+ // PNG Informationen auslesen
+ int BitDepth;
+ int ColorType;
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)&Width, (png_uint_32 *)&Height, &BitDepth, &ColorType, NULL, NULL, NULL);
+
+ // PNG-ColorType in BS ColorFormat konvertieren.
+ if (ColorType & PNG_COLOR_MASK_ALPHA || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ ColorFormat = GraphicEngine::CF_ARGB32;
+ else
+ ColorFormat = GraphicEngine::CF_RGB24;
+
+ // Die Strukturen freigeben
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ return true;
+
+}
+
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::ImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height) {
+ return DoImageProperties(FileDataPtr, FileSize, ColorFormat, Width, Height);
+}
+
+// -----------------------------------------------------------------------------
+// Header überprüfen
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::DoIsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
+ if (FileSize > 8)
+ return png_check_sig(const_cast<byte *>(FileDataPtr), 8) ? true : false;
+ else
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool PNGLoader::IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {
+ return DoIsCorrectImageFormat(FileDataPtr, FileSize);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/pngloader.h b/engines/sword25/gfx/image/pngloader.h
new file mode 100644
index 0000000000..44c31b267c
--- /dev/null
+++ b/engines/sword25/gfx/image/pngloader.h
@@ -0,0 +1,79 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_PNGLoader
+ ------------
+ BS_ImageLoader-Klasse zum Laden von PNG-Dateien
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_PNGLOADER2_H
+#define SWORD25_PNGLOADER2_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/imageloader.h"
+
+namespace Sword25 {
+
+// Klassendefinition
+class PNGLoader : public ImageLoader {
+public:
+ static ImageLoader *CreateInstance() {
+ return (ImageLoader *) new PNGLoader();
+ }
+
+ // Alle virtuellen Methoden von BS_ImageLoader sind hier als static-Methode implementiert, damit sie von BS_B25SLoader aufgerufen werden können.
+ // Die virtuellen Methoden rufen diese Methoden auf.
+ static bool DoIsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
+ static bool DoDecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr,
+ int &Width, int &Height, int &Pitch);
+ static bool DoImageProperties(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
+
+protected:
+ PNGLoader();
+ bool DecodeImage(const byte *pFileData, uint FileSize,
+ GraphicEngine::COLOR_FORMATS ColorFormat,
+ byte *&pUncompressedData,
+ int &Width, int &Height,
+ int &Pitch);
+ bool IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize);
+ bool ImageProperties(const byte *FileDatePtr, uint FileSize, GraphicEngine::COLOR_FORMATS &ColorFormat, int &Width, int &Height);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/renderedimage.cpp b/engines/sword25/gfx/image/renderedimage.cpp
new file mode 100644
index 0000000000..96b6139a59
--- /dev/null
+++ b/engines/sword25/gfx/image/renderedimage.cpp
@@ -0,0 +1,398 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "common/system.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEREDIMAGE"
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+RenderedImage::RenderedImage(const Common::String &filename, bool &result) :
+ _data(0),
+ _width(0),
+ _height(0) {
+ result = false;
+
+ PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+
+ // Datei laden
+ byte *pFileData;
+ uint fileSize;
+ if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ GraphicEngine::COLOR_FORMATS colorFormat;
+ int pitch;
+ if (!ImageLoader::ExtractImageProperties(pFileData, fileSize, colorFormat, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ delete[] pFileData;
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ if (!ImageLoader::LoadImage(pFileData, fileSize, GraphicEngine::CF_ARGB32, _data, _width, _height, pitch)) {
+ BS_LOG_ERRORLN("Could not decode image.");
+ delete[] pFileData;
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ _doCleanup = true;
+
+ result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+RenderedImage::RenderedImage(uint width, uint height, bool &result) :
+ _width(width),
+ _height(height) {
+
+ _data = new byte[width * height * 4];
+ Common::set_to(_data, &_data[width * height * 4], 0);
+
+ _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+
+ _doCleanup = true;
+
+ result = true;
+ return;
+}
+
+RenderedImage::RenderedImage() : _width(0), _height(0), _data(0) {
+ _backSurface = (static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx")))->getSurface();
+
+ _doCleanup = false;
+
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+RenderedImage::~RenderedImage() {
+ if (_doCleanup)
+ delete[] _data;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ // Überprüfen, ob PixelData ausreichend viele Pixel enthält um ein Bild der Größe Width * Height zu erzeugen
+ if (size < static_cast<uint>(_width * _height * 4)) {
+ BS_LOG_ERRORLN("PixelData vector is too small to define a 32 bit %dx%d image.", _width, _height);
+ return false;
+ }
+
+ const byte *in = &pixeldata[offset];
+ byte *out = _data;
+
+ for (int i = 0; i < _height; i++) {
+ memcpy(out, in, _width * 4);
+ out += _width * 4;
+ in += stride;
+ }
+
+ return true;
+}
+
+void RenderedImage::replaceContent(byte *pixeldata, int width, int height) {
+ _width = width;
+ _height = height;
+ _data = pixeldata;
+}
+// -----------------------------------------------------------------------------
+
+uint RenderedImage::getPixel(int x, int y) {
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderedImage::blit(int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) {
+ int ca = (color >> 24) & 0xff;
+
+ // Check if we need to draw anything at all
+ if (ca == 0)
+ return true;
+
+ int cr = (color >> 16) & 0xff;
+ int cg = (color >> 8) & 0xff;
+ int cb = (color >> 0) & 0xff;
+
+ // Compensate for transparency. Since we're coming
+ // down to 255 alpha, we just compensate for the colors here
+ if (ca != 255) {
+ cr = cr * ca >> 8;
+ cg = cg * ca >> 8;
+ cb = cb * ca >> 8;
+ }
+
+ // Create an encapsulating surface for the data
+ Graphics::Surface srcImage;
+ srcImage.bytesPerPixel = 4;
+ srcImage.pitch = _width * 4;
+ srcImage.w = _width;
+ srcImage.h = _height;
+ srcImage.pixels = _data;
+
+ if (pPartRect) {
+ srcImage.pixels = &_data[pPartRect->top * srcImage.pitch + pPartRect->left * 4];
+ srcImage.w = pPartRect->right - pPartRect->left;
+ srcImage.h = pPartRect->bottom - pPartRect->top;
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
+ pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
+ } else {
+
+ debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
+ srcImage.w, srcImage.h, color, width, height);
+ }
+
+ if (width == -1)
+ width = srcImage.w;
+ if (height == -1)
+ height = srcImage.h;
+
+#ifdef SCALING_TESTING
+ // Hardcode scaling to 66% to test scaling
+ width = width * 2 / 3;
+ height = height * 2 / 3;
+#endif
+
+ Graphics::Surface *img;
+ Graphics::Surface *imgScaled = NULL;
+ byte *savedPixels = NULL;
+ if ((width != srcImage.w) || (height != srcImage.h)) {
+ // Scale the image
+ img = imgScaled = scale(srcImage, width, height);
+ savedPixels = (byte *)img->pixels;
+ } else {
+ img = &srcImage;
+ }
+
+ // Handle off-screen clipping
+ if (posY < 0) {
+ img->h = MAX(0, (int)img->h - -posY);
+ img->pixels = (byte *)img->pixels + img->pitch * -posY;
+ posY = 0;
+ }
+
+ if (posX < 0) {
+ img->w = MAX(0, (int)img->w - -posX);
+ img->pixels = (byte *)img->pixels + (-posX * 4);
+ posX = 0;
+ }
+
+ img->w = CLIP((int)img->w, 0, (int)MAX((int)_backSurface->w - posX, 0));
+ img->h = CLIP((int)img->h, 0, (int)MAX((int)_backSurface->h - posY, 0));
+
+ if ((img->w > 0) && (img->h > 0)) {
+ int xp = 0, yp = 0;
+
+ int inStep = 4;
+ int inoStep = img->pitch;
+ if (flipping & Image::FLIP_V) {
+ inStep = -inStep;
+ xp = img->w - 1;
+ }
+
+ if (flipping & Image::FLIP_H) {
+ inoStep = -inoStep;
+ yp = img->h - 1;
+ }
+
+ byte *ino = (byte *)img->getBasePtr(xp, yp);
+ byte *outo = (byte *)_backSurface->getBasePtr(posX, posY);
+ byte *in, *out;
+
+ for (int i = 0; i < img->h; i++) {
+ out = outo;
+ in = ino;
+ for (int j = 0; j < img->w; j++) {
+ int r = in[0];
+ int g = in[1];
+ int b = in[2];
+ int a = in[3];
+ in += inStep;
+
+ if (ca != 255) {
+ a = a * ca >> 8;
+ }
+
+ switch (a) {
+ case 0: // Full transparency
+ out += 4;
+ break;
+ case 255: // Full opacity
+ if (cr != 255)
+ *out++ = (r * cr) >> 8;
+ else
+ *out++ = r;
+
+ if (cg != 255)
+ *out++ = (g * cg) >> 8;
+ else
+ *out++ = g;
+
+ if (cb != 255)
+ *out++ = (b * cb) >> 8;
+ else
+ *out++ = b;
+
+ *out++ = a;
+ break;
+
+ default: // alpha blending
+ if (cr != 255)
+ *out += ((r - *out) * a * cr) >> 16;
+ else
+ *out += ((r - *out) * a) >> 8;
+ out++;
+ if (cg != 255)
+ *out += ((g - *out) * a * cg) >> 16;
+ else
+ *out += ((g - *out) * a) >> 8;
+ out++;
+ if (cb != 255)
+ *out += ((b - *out) * a * cb) >> 16;
+ else
+ *out += ((b - *out) * a) >> 8;
+ out++;
+ *out = 255;
+ out++;
+ }
+ }
+ outo += _backSurface->pitch;
+ ino += inoStep;
+ }
+
+ g_system->copyRectToScreen((byte *)_backSurface->getBasePtr(posX, posY), _backSurface->pitch, posX, posY,
+ img->w, img->h);
+ }
+
+ if (imgScaled) {
+ imgScaled->pixels = savedPixels;
+ imgScaled->free();
+ delete imgScaled;
+ }
+
+ return true;
+}
+
+/**
+ * Scales a passed surface, creating a new surface with the result
+ * @param srcImage Source image to scale
+ * @param scaleFactor Scale amount. Must be between 0 and 1.0 (but not zero)
+ * @remarks Caller is responsible for freeing the returned surface
+ */
+Graphics::Surface *RenderedImage::scale(const Graphics::Surface &srcImage, int xSize, int ySize) {
+ Graphics::Surface *s = new Graphics::Surface();
+ s->create(xSize, ySize, srcImage.bytesPerPixel);
+
+ int *horizUsage = scaleLine(xSize, srcImage.w);
+ int *vertUsage = scaleLine(ySize, srcImage.h);
+
+ // Loop to create scaled version
+ for (int yp = 0; yp < ySize; ++yp) {
+ const byte *srcP = (const byte *)srcImage.getBasePtr(0, vertUsage[yp]);
+ byte *destP = (byte *)s->getBasePtr(0, yp);
+
+ for (int xp = 0; xp < xSize; ++xp) {
+ const byte *tempSrcP = srcP + (horizUsage[xp] * srcImage.bytesPerPixel);
+ for (int byteCtr = 0; byteCtr < srcImage.bytesPerPixel; ++byteCtr) {
+ *destP++ = *tempSrcP++;
+ }
+ }
+ }
+
+ // Delete arrays and return surface
+ delete[] horizUsage;
+ delete[] vertUsage;
+ return s;
+}
+
+/**
+ * Returns an array indicating which pixels of a source image horizontally or vertically get
+ * included in a scaled image
+ */
+int *RenderedImage::scaleLine(int size, int srcSize) {
+ int scale = 100 * size / srcSize;
+ assert(scale > 0);
+ int *v = new int[size];
+ Common::set_to(v, &v[size], 0);
+
+ int distCtr = 0;
+ int *destP = v;
+ for (int distIndex = 0; distIndex < srcSize; ++distIndex) {
+ distCtr += scale;
+ while (distCtr >= 100) {
+ assert(destP < &v[size]);
+ *destP++ = distIndex;
+ distCtr -= 100;
+ }
+ }
+
+ return v;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/renderedimage.h b/engines/sword25/gfx/image/renderedimage.h
new file mode 100644
index 0000000000..a9f2f1823c
--- /dev/null
+++ b/engines/sword25/gfx/image/renderedimage.h
@@ -0,0 +1,121 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDERED_IMAGE_H
+#define SWORD25_RENDERED_IMAGE_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/graphicengine.h"
+
+namespace Sword25 {
+
+class RenderedImage : public Image {
+public:
+ RenderedImage(const Common::String &filename, bool &result);
+
+ /**
+ @brief Erzeugt ein leeres BS_RenderedImage
+
+ @param Width die Breite des zu erzeugenden Bildes.
+ @param Height die Höhe des zu erzeugenden Bildes
+ @param Result gibt dem Aufrufer bekannt, ob der Konstruktor erfolgreich ausgeführt wurde. Wenn es nach dem Aufruf false enthalten sollte,
+ dürfen keine Methoden am Objekt aufgerufen werden und das Objekt ist sofort zu zerstören.
+ */
+ RenderedImage(uint width, uint height, bool &result);
+ RenderedImage();
+
+ virtual ~RenderedImage();
+
+ virtual int getWidth() const {
+ return _width;
+ }
+ virtual int getHeight() const {
+ return _height;
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = Image::FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+ virtual bool fill(const Common::Rect *pFillRect, uint color);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset = 0, uint stride = 0);
+ void replaceContent(byte *pixeldata, int width, int height);
+ virtual uint getPixel(int x, int y);
+
+ virtual bool isBlitSource() const {
+ return true;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return true;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return true;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return true;
+ }
+ virtual bool isSetContentAllowed() const {
+ return true;
+ }
+
+ static Graphics::Surface *scale(const Graphics::Surface &srcImage, int xSize, int ySize);
+private:
+ byte *_data;
+ int _width;
+ int _height;
+ bool _doCleanup;
+
+ Graphics::Surface *_backSurface;
+
+ static int *scaleLine(int size, int srcSize);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/swimage.cpp b/engines/sword25/gfx/image/swimage.cpp
new file mode 100644
index 0000000000..ac4463ea16
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.cpp
@@ -0,0 +1,134 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/package/packagemanager.h"
+#include "sword25/gfx/image/imageloader.h"
+#include "sword25/gfx/image/swimage.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "SWIMAGE"
+
+
+// -----------------------------------------------------------------------------
+// CONSTRUCTION / DESTRUCTION
+// -----------------------------------------------------------------------------
+
+SWImage::SWImage(const Common::String &filename, bool &result) :
+ _imageDataPtr(0),
+ _width(0),
+ _height(0) {
+ result = false;
+
+ PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // Datei laden
+ byte *pFileData;
+ uint fileSize;
+ if (!(pFileData = (byte *)pPackage->getFile(filename, &fileSize))) {
+ BS_LOG_ERRORLN("File \"%s\" could not be loaded.", filename.c_str());
+ return;
+ }
+
+ // Bildeigenschaften bestimmen
+ GraphicEngine::COLOR_FORMATS colorFormat;
+ int pitch;
+ if (!ImageLoader::ExtractImageProperties(pFileData, fileSize, colorFormat, _width, _height)) {
+ BS_LOG_ERRORLN("Could not read image properties.");
+ return;
+ }
+
+ // Das Bild dekomprimieren
+ byte *pUncompressedData;
+ if (!ImageLoader::LoadImage(pFileData, fileSize, GraphicEngine::CF_ARGB32, pUncompressedData, _width, _height, pitch)) {
+ BS_LOG_ERRORLN("Could not decode image.");
+ return;
+ }
+
+ // Dateidaten freigeben
+ delete[] pFileData;
+
+ _imageDataPtr = (uint *)pUncompressedData;
+
+ result = true;
+ return;
+}
+
+// -----------------------------------------------------------------------------
+
+SWImage::~SWImage() {
+ delete[] _imageDataPtr;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool SWImage::blit(int posX, int posY,
+ int flipping,
+ Common::Rect *pPartRect,
+ uint color,
+ int width, int height) {
+ BS_LOG_ERRORLN("Blit() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool SWImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+bool SWImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+uint SWImage::getPixel(int x, int y) {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ return _imageDataPtr[_width * y + x];
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/swimage.h b/engines/sword25/gfx/image/swimage.h
new file mode 100644
index 0000000000..caf6bdcc71
--- /dev/null
+++ b/engines/sword25/gfx/image/swimage.h
@@ -0,0 +1,107 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_SWIMAGE_H
+#define SWORD25_SWIMAGE_H
+
+// -----------------------------------------------------------------------------
+// INCLUDES
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "sword25/gfx/graphicengine.h"
+
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// CLASS DEFINITION
+// -----------------------------------------------------------------------------
+
+class SWImage : public Image {
+public:
+ SWImage(const Common::String &filename, bool &result);
+ virtual ~SWImage();
+
+ virtual int getWidth() const {
+ return _width;
+ }
+ virtual int getHeight() const {
+ return _height;
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = Image::FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+ virtual bool fill(const Common::Rect *fillRectPtr, uint color);
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual uint getPixel(int x, int y);
+
+ virtual bool isBlitSource() const {
+ return false;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return false;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return false;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return false;
+ }
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+private:
+ uint *_imageDataPtr;
+
+ int _width;
+ int _height;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp
new file mode 100644
index 0000000000..c2a80cb5f2
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.cpp
@@ -0,0 +1,637 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/gfx/image/art.h"
+#include "sword25/gfx/image/vectorimage.h"
+#include "sword25/gfx/image/renderedimage.h"
+
+#include "graphics/colormasks.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "VECTORIMAGE"
+
+#define BEZSMOOTHNESS 0.5
+
+// -----------------------------------------------------------------------------
+// SWF Datentypen
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Bitstream Hilfsklasse
+// -----------------------------------------------------------------------------
+// Das Parsen von SWF-Dateien erfordert sowohl bitweises Auslesen als auch an
+// Bytegrenzen ausgerichtetes Lesen.
+// Diese Klasse ist speziell dafür ausgestattet.
+// -----------------------------------------------------------------------------
+
+class VectorImage::SWFBitStream {
+public:
+ SWFBitStream(const byte *pData, uint dataSize) :
+ m_Pos(pData), m_End(pData + dataSize), m_WordMask(0)
+ {}
+
+ inline uint32 getBits(uint bitCount) {
+ if (bitCount == 0 || bitCount > 32) {
+ error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time");
+ }
+
+ uint32 value = 0;
+ while (bitCount) {
+ if (m_WordMask == 0)
+ flushByte();
+
+ value <<= 1;
+ value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
+ m_WordMask >>= 1;
+
+ --bitCount;
+ }
+
+ return value;
+ }
+
+ inline int32 getSignedBits(uint bitCount) {
+ // Bits einlesen
+ uint32 temp = getBits(bitCount);
+
+ // Falls das Sign-Bit gesetzt ist, den Rest des Rückgabewertes mit 1-Bits auffüllen (Sign Extension)
+ if (temp & 1 << (bitCount - 1))
+ return (0xffffffff << bitCount) | temp;
+ else
+ return temp;
+ }
+
+ inline uint32 getUInt32() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+ uint32 byte3 = getByte();
+ uint32 byte4 = getByte();
+
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+ }
+
+ inline uint16 getUInt16() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+
+ return byte1 | (byte2 << 8);
+ }
+
+ inline byte getByte() {
+ flushByte();
+ byte value = m_Word;
+ m_WordMask = 0;
+ flushByte();
+
+ return value;
+ }
+
+ inline void flushByte() {
+ if (m_WordMask != 128) {
+ if (m_Pos >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Word = *m_Pos++;
+ m_WordMask = 128;
+ }
+ }
+ }
+
+ inline void skipBytes(uint skipLength) {
+ flushByte();
+ if (m_Pos + skipLength >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Pos += skipLength;
+ m_Word = *(m_Pos - 1);
+ }
+ }
+
+private:
+ const byte *m_Pos;
+ const byte *m_End;
+
+ byte m_Word;
+ uint m_WordMask;
+};
+
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace {
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+const uint32 MAX_ACCEPTED_FLASH_VERSION = 3; // Die höchste Flash-Dateiversion, die vom Lader akzeptiert wird
+
+
+// -----------------------------------------------------------------------------
+// Konvertiert SWF-Rechteckdaten in einem Bitstrom in Common::Rect-Objekte
+// -----------------------------------------------------------------------------
+
+Common::Rect flashRectToBSRect(VectorImage::SWFBitStream &bs) {
+ bs.flushByte();
+
+ // Feststellen mit wie vielen Bits die einzelnen Komponenten kodiert sind
+ uint32 bitsPerValue = bs.getBits(5);
+
+ // Die einzelnen Komponenten einlesen
+ int32 xMin = bs.getSignedBits(bitsPerValue);
+ int32 xMax = bs.getSignedBits(bitsPerValue);
+ int32 yMin = bs.getSignedBits(bitsPerValue);
+ int32 yMax = bs.getSignedBits(bitsPerValue);
+
+ return Common::Rect(xMin, yMin, xMax + 1, yMax + 1);
+}
+
+// -----------------------------------------------------------------------------
+// Berechnet die Bounding-Box eines BS_VectorImageElement
+// -----------------------------------------------------------------------------
+
+Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) {
+ double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
+
+ for (int j = vectorImageElement.getPathCount() - 1; j >= 0; j--) {
+ ArtBpath *bez = vectorImageElement.getPathInfo(j).getVec();
+ ArtVpath *vec = art_bez_path_to_vec(bez, 0.5);
+
+ if (vec[0].code == ART_END) {
+ continue;
+ } else {
+ x0 = x1 = vec[0].x;
+ y0 = y1 = vec[0].y;
+ for (int i = 1; vec[i].code != ART_END; i++) {
+ if (vec[i].x < x0) x0 = vec[i].x;
+ if (vec[i].x > x1) x1 = vec[i].x;
+ if (vec[i].y < y0) y0 = vec[i].y;
+ if (vec[i].y > y1) y1 = vec[i].y;
+ }
+ }
+ free(vec);
+ }
+
+ return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1);
+}
+
+}
+
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname) : _pixelData(0), _fname(fname) {
+ success = false;
+
+ // Bitstream-Objekt erzeugen
+ // Im Folgenden werden die Dateidaten aus diesem ausgelesen.
+ SWFBitStream bs(pFileData, fileSize);
+
+ // SWF-Signatur überprüfen
+ uint32 signature[3];
+ signature[0] = bs.getByte();
+ signature[1] = bs.getByte();
+ signature[2] = bs.getByte();
+ if (signature[0] != 'F' ||
+ signature[1] != 'W' ||
+ signature[2] != 'S') {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // Versionsangabe überprüfen
+ uint32 version = bs.getByte();
+ if (version > MAX_ACCEPTED_FLASH_VERSION) {
+ BS_LOG_ERRORLN("File is of version %d. Highest accepted version is %d.", version, MAX_ACCEPTED_FLASH_VERSION);
+ return;
+ }
+
+ // Dateigröße auslesen und mit der tatsächlichen Größe vergleichen
+ uint32 storedFileSize = bs.getUInt32();
+ if (storedFileSize != fileSize) {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // SWF-Maße auslesen
+ Common::Rect movieRect = flashRectToBSRect(bs);
+
+ // Framerate und Frameanzahl auslesen
+ /* uint32 frameRate = */
+ bs.getUInt16();
+ /* uint32 frameCount = */
+ bs.getUInt16();
+
+ // Tags parsen
+ // Da wir uns nur für das erste DefineShape-Tag interessieren
+ bool keepParsing = true;
+ while (keepParsing) {
+ // Tags beginnen immer an Bytegrenzen
+ bs.flushByte();
+
+ // Tagtyp und Länge auslesen
+ uint16 tagTypeAndLength = bs.getUInt16();
+ uint32 tagType = tagTypeAndLength >> 6;
+ uint32 tagLength = tagTypeAndLength & 0x3f;
+ if (tagLength == 0x3f)
+ tagLength = bs.getUInt32();
+
+ switch (tagType) {
+ case 2:
+ // DefineShape
+ success = parseDefineShape(2, bs);
+ return;
+ case 22:
+ // DefineShape2
+ success = parseDefineShape(2, bs);
+ return;
+ case 32:
+ success = parseDefineShape(3, bs);
+ return;
+ default:
+ // Unbekannte Tags ignorieren
+ bs.skipBytes(tagLength);
+ }
+ }
+
+ // Die Ausführung darf nicht an dieser Stelle ankommen: Entweder es wird ein Shape gefunden, dann wird die Funktion mit vorher verlassen, oder
+ // es wird keines gefunden, dann tritt eine Exception auf sobald über das Ende der Datei hinaus gelesen wird.
+ BS_ASSERT(false);
+}
+
+VectorImage::~VectorImage() {
+ for (int j = _elements.size() - 1; j >= 0; j--)
+ for (int i = _elements[j].getPathCount() - 1; i >= 0; i--)
+ if (_elements[j].getPathInfo(i).getVec())
+ free(_elements[j].getPathInfo(i).getVec());
+
+ if (_pixelData)
+ free(_pixelData);
+}
+
+
+ArtBpath *ensureBezStorage(ArtBpath *bez, int nodes, int *allocated) {
+ if (*allocated <= nodes) {
+ (*allocated) += 20;
+
+ return art_renew(bez, ArtBpath, *allocated);
+ }
+
+ return bez;
+}
+
+ArtBpath *VectorImage::storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated) {
+ (*bezNodes)++;
+
+ bez = ensureBezStorage(bez, *bezNodes, bezAllocated);
+ bez[*bezNodes].code = ART_END;
+
+ ArtBpath *bez1 = art_new(ArtBpath, *bezNodes + 1);
+
+ for (int i = 0; i <= *bezNodes; i++)
+ bez1[i] = bez[i];
+
+ _elements.back()._pathInfos.push_back(VectorPathInfo(bez1, *bezNodes, lineStyle, fillStyle0, fillStyle1));
+
+ return bez;
+}
+
+bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {
+ /*uint32 shapeID = */bs.getUInt16();
+
+ // Bounding Box auslesen
+ _boundingBox = flashRectToBSRect(bs);
+
+ // Erstes Image-Element erzeugen
+ _elements.resize(1);
+
+ // Styles einlesen
+ uint numFillBits;
+ uint numLineBits;
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+
+ uint lineStyle = 0;
+ uint fillStyle0 = 0;
+ uint fillStyle1 = 0;
+
+ // Shaperecord parsen
+ // ------------------
+
+ double curX = 0;
+ double curY = 0;
+ int bezNodes = 0;
+ int bezAllocated = 10;
+ ArtBpath *bez = art_new(ArtBpath, bezAllocated);
+
+ bool endOfShapeDiscovered = false;
+ while (!endOfShapeDiscovered) {
+ uint32 typeFlag = bs.getBits(1);
+
+ // Non-Edge Record
+ if (typeFlag == 0) {
+ // Feststellen welche Parameter gesetzt werden
+ uint32 stateNewStyles = bs.getBits(1);
+ uint32 stateLineStyle = bs.getBits(1);
+ uint32 stateFillStyle1 = bs.getBits(1);
+ uint32 stateFillStyle0 = bs.getBits(1);
+ uint32 stateMoveTo = bs.getBits(1);
+
+ uint prevLineStyle = lineStyle;
+ uint prevFillStyle0 = fillStyle0;
+ uint prevFillStyle1 = fillStyle1;
+
+ // End der Shape-Definition erreicht?
+ if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {
+ endOfShapeDiscovered = true;
+ // Parameter dekodieren
+ } else {
+ if (stateMoveTo) {
+ uint32 moveToBits = bs.getBits(5);
+ curX = bs.getSignedBits(moveToBits);
+ curY = bs.getSignedBits(moveToBits);
+ }
+
+ if (stateFillStyle0) {
+ if (numFillBits > 0)
+ fillStyle0 = bs.getBits(numFillBits);
+ else
+ fillStyle0 = 0;
+ }
+
+ if (stateFillStyle1) {
+ if (numFillBits > 0)
+ fillStyle1 = bs.getBits(numFillBits);
+ else
+ fillStyle1 = 0;
+ }
+
+ if (stateLineStyle) {
+ if (numLineBits)
+ lineStyle = bs.getBits(numLineBits);
+ else
+ numLineBits = 0;
+ }
+
+ // Ein neuen Pfad erzeugen, es sei denn, es wurden nur neue Styles definiert
+ if (stateLineStyle || stateFillStyle0 || stateFillStyle1 || stateMoveTo) {
+ // Store previous curve if any
+ if (bezNodes) {
+ bez = storeBez(bez, prevLineStyle, prevFillStyle0, prevFillStyle1, &bezNodes, &bezAllocated);
+ }
+
+ // Start new curve
+ bez = ensureBezStorage(bez, 1, &bezAllocated);
+ bez[0].code = ART_MOVETO_OPEN;
+ bez[0].x3 = curX;
+ bez[0].y3 = curY;
+ bezNodes = 0;
+ }
+
+ if (stateNewStyles) {
+ // An dieser Stelle werden in Flash die alten Style-Definitionen verworfen und mit den neuen überschrieben.
+ // Es wird ein neues Element begonnen.
+ _elements.resize(_elements.size() + 1);
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+ }
+ }
+ } else {
+ // Edge Record
+ uint32 edgeFlag = bs.getBits(1);
+ uint32 numBits = bs.getBits(4) + 2;
+
+ // Curved edge
+ if (edgeFlag == 0) {
+ double controlDeltaX = bs.getSignedBits(numBits);
+ double controlDeltaY = bs.getSignedBits(numBits);
+ double anchorDeltaX = bs.getSignedBits(numBits);
+ double anchorDeltaY = bs.getSignedBits(numBits);
+
+ double controlX = curX + controlDeltaX;
+ double controlY = curY + controlDeltaY;
+ double newX = controlX + anchorDeltaX;
+ double newY = controlY + anchorDeltaY;
+
+#define WEIGHT (2.0/3.0)
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_CURVETO;
+ bez[bezNodes].x1 = WEIGHT * controlX + (1 - WEIGHT) * curX;
+ bez[bezNodes].y1 = WEIGHT * controlY + (1 - WEIGHT) * curY;
+ bez[bezNodes].x2 = WEIGHT * controlX + (1 - WEIGHT) * newX;
+ bez[bezNodes].y2 = WEIGHT * controlY + (1 - WEIGHT) * newY;
+ bez[bezNodes].x3 = newX;
+ bez[bezNodes].y3 = newY;
+
+ curX = newX;
+ curY = newY;
+ } else {
+ // Staight edge
+ int32 deltaX = 0;
+ int32 deltaY = 0;
+
+ uint32 generalLineFlag = bs.getBits(1);
+ if (generalLineFlag) {
+ deltaX = bs.getSignedBits(numBits);
+ deltaY = bs.getSignedBits(numBits);
+ } else {
+ uint32 vertLineFlag = bs.getBits(1);
+ if (vertLineFlag)
+ deltaY = bs.getSignedBits(numBits);
+ else
+ deltaX = bs.getSignedBits(numBits);
+ }
+
+ curX += deltaX;
+ curY += deltaY;
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_LINETO;
+ bez[bezNodes].x3 = curX;
+ bez[bezNodes].y3 = curY;
+ }
+ }
+ }
+
+ // Store last curve
+ if (bezNodes)
+ bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated);
+
+ free(bez);
+
+ // Bounding-Boxes der einzelnen Elemente berechnen
+ Common::Array<VectorImageElement>::iterator it = _elements.begin();
+ for (; it != _elements.end(); ++it)
+ it->_boundingBox = CalculateBoundingBox(*it);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits) {
+ bs.flushByte();
+
+ // Fillstyles parsen
+ // -----------------
+
+ // Anzahl an Fillstyles bestimmen
+ uint fillStyleCount = bs.getByte();
+ if (fillStyleCount == 0xff)
+ fillStyleCount = bs.getUInt16();
+
+ // Alle Fillstyles einlesen, falls ein Fillstyle mit Typ != 0 gefunden wird, wird das Parsen abgebrochen.
+ // Es wird nur "solid fill" (Typ 0) unterstützt.
+ _elements.back()._fillStyles.reserve(fillStyleCount);
+ for (uint i = 0; i < fillStyleCount; ++i) {
+ byte type = bs.getByte();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+
+ if (type != 0)
+ return false;
+
+ _elements.back()._fillStyles.push_back(color);
+ }
+
+ // Linestyles parsen
+ // -----------------
+
+ // Anzahl an Linestyles bestimmen
+ uint lineStyleCount = bs.getByte();
+ if (lineStyleCount == 0xff)
+ lineStyleCount = bs.getUInt16();
+
+ // Alle Linestyles einlesen
+ _elements.back()._lineStyles.reserve(lineStyleCount);
+ for (uint i = 0; i < lineStyleCount; ++i) {
+ double width = bs.getUInt16();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+
+ _elements.back()._lineStyles.push_back(VectorImageElement::LineStyleType(width, color));
+ }
+
+ // Bitbreite für die folgenden Styleindizes auslesen
+ numFillBits = bs.getBits(4);
+ numLineBits = bs.getBits(4);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+
+
+// -----------------------------------------------------------------------------
+
+uint VectorImage::getPixel(int x, int y) {
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return 0;
+}
+
+bool VectorImage::blit(int posX, int posY,
+ int flipping,
+ Common::Rect *pPartRect,
+ uint color,
+ int width, int height) {
+ static VectorImage *oldThis = 0;
+ static int oldWidth = -2;
+ static int oldHeight = -2;
+
+ // Falls Breite oder Höhe 0 sind, muss nichts dargestellt werden.
+ if (width == 0 || height == 0)
+ return true;
+
+ // Feststellen, ob das alte Bild im Cache nicht wiederbenutzt werden kann und neu Berechnet werden muss
+ if (!(oldThis == this && oldWidth == width && oldHeight == height)) {
+ render(width, height);
+
+ oldThis = this;
+ oldHeight = height;
+ oldWidth = width;
+ }
+
+ RenderedImage *rend = new RenderedImage();
+
+ rend->replaceContent(_pixelData, width, height);
+ rend->blit(posX, posY, flipping, pPartRect, color, width, height);
+
+ delete rend;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h
new file mode 100644
index 0000000000..3477463b43
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimage.h
@@ -0,0 +1,237 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_VECTORIMAGE_H
+#define SWORD25_VECTORIMAGE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/image/image.h"
+#include "common/rect.h"
+
+#include "art.h"
+
+namespace Sword25 {
+
+class VectorImage;
+
+/**
+ @brief Pfadinformationen zu BS_VectorImageElement Objekten
+
+ Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
+ gespeichert werden.
+*/
+
+class VectorPathInfo {
+public:
+ VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) :
+ _vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {}
+
+ VectorPathInfo() {
+ _lineStyle = _fillStyle0 = _fillStyle1 = _len = 0;
+ _vec = 0;
+ }
+
+ ArtBpath *getVec() const {
+ return _vec;
+ }
+ int getVecLen() const {
+ return _len;
+ }
+ uint getLineStyle() const {
+ return _lineStyle;
+ }
+ uint getFillStyle0() const {
+ return _fillStyle0;
+ }
+ uint getFillStyle1() const {
+ return _fillStyle1;
+ }
+
+private:
+ ArtBpath *_vec;
+ uint _lineStyle;
+ uint _fillStyle0;
+ uint _fillStyle1;
+ uint _len;
+};
+
+/**
+ @brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
+ Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
+*/
+class VectorImageElement {
+ friend class VectorImage;
+public:
+ uint getPathCount() const {
+ return _pathInfos.size();
+ }
+ const VectorPathInfo &getPathInfo(uint pathNr) const {
+ BS_ASSERT(pathNr < getPathCount());
+ return _pathInfos[pathNr];
+ }
+
+ double getLineStyleWidth(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].width;
+ }
+
+ uint getLineStyleCount() const {
+ return _lineStyles.size();
+ }
+
+ uint32 getLineStyleColor(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].color;
+ }
+
+ uint getFillStyleCount() const {
+ return _fillStyles.size();
+ }
+
+ uint32 getFillStyleColor(uint fillStyle) const {
+ BS_ASSERT(fillStyle < _fillStyles.size());
+ return _fillStyles[fillStyle];
+ }
+
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+private:
+ struct LineStyleType {
+ LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {}
+ LineStyleType() {
+ width = 0;
+ color = 0;
+ }
+ double width;
+ uint32 color;
+ };
+
+ Common::Array<VectorPathInfo> _pathInfos;
+ Common::Array<LineStyleType> _lineStyles;
+ Common::Array<uint32> _fillStyles;
+ Common::Rect _boundingBox;
+};
+
+
+/**
+ @brief Eine Vektorgraphik
+
+ Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
+*/
+
+class VectorImage : public Image {
+public:
+ VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname);
+ ~VectorImage();
+
+ uint getElementCount() const {
+ return _elements.size();
+ }
+ const VectorImageElement &getElement(uint elementNr) const {
+ BS_ASSERT(elementNr < _elements.size());
+ return _elements[elementNr];
+ }
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+ //
+ // Die abstrakten Methoden von BS_Image
+ //
+ virtual int getWidth() const {
+ return _boundingBox.width();
+ }
+ virtual int getHeight() const {
+ return _boundingBox.height();
+ }
+ virtual GraphicEngine::COLOR_FORMATS getColorFormat() const {
+ return GraphicEngine::CF_ARGB32;
+ }
+ virtual bool fill(const Common::Rect *pFillRect = 0, uint color = BS_RGB(0, 0, 0));
+
+ void render(int width, int height);
+
+ virtual uint getPixel(int x, int y);
+ virtual bool isBlitSource() const {
+ return true;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return true;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return true;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return true;
+ }
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+
+ class SWFBitStream;
+
+private:
+ bool parseDefineShape(uint shapeType, SWFBitStream &bs);
+ bool parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits);
+
+ ArtBpath *storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated);
+ Common::Array<VectorImageElement> _elements;
+ Common::Rect _boundingBox;
+
+ byte *_pixelData;
+
+ Common::String _fname;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp
new file mode 100644
index 0000000000..16d1abf9f9
--- /dev/null
+++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp
@@ -0,0 +1,461 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code contains portions of Libart_LGPL - library of basic graphic primitives
+ *
+ * Copyright (c) 1998 Raph Levien
+ *
+ * Licensed under GNU LGPL v2
+ *
+ */
+
+/*
+ * This code contains portions of Swfdec
+ *
+ * Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "art.h"
+
+#include "sword25/gfx/image/vectorimage.h"
+#include "graphics/colormasks.h"
+
+namespace Sword25 {
+
+void art_rgb_fill_run1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n) {
+ int i;
+
+ if (r == g && g == b && r == 255) {
+ memset(buf, g, n + n + n + n);
+ } else {
+ uint32 *alt = (uint32 *)buf;
+ uint32 color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, r, g, b);
+
+ for (i = 0; i < n; i++)
+ *alt++ = color;
+ }
+}
+
+void art_rgb_run_alpha1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) {
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++) {
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = MIN(v + alpha, 0xff);
+ }
+}
+
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+void art_rgb_svp_alpha1(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ uint32 color,
+ art_u8 *buf, int rowstride) {
+ ArtRgbSVPAlphaData data;
+ byte r, g, b, alpha;
+ int i;
+ int a, da;
+
+ Graphics::colorToARGB<Graphics::ColorMasks<8888> >(color, alpha, r, g, b);
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++) {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
+ else
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
+}
+
+static int art_vpath_len(ArtVpath *a) {
+ int i;
+
+ for (i = 0; a[i].code != ART_END; i++);
+ return i;
+}
+
+ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
+ ArtVpath *dest;
+ ArtVpath *p;
+ int len_a, len_b;
+
+ len_a = art_vpath_len(a);
+ len_b = art_vpath_len(b);
+ dest = art_new(ArtVpath, len_a + len_b + 1);
+ p = dest;
+
+ for (int i = 0; i < len_a; i++)
+ *p++ = *a++;
+ for (int i = 0; i <= len_b; i++)
+ *p++ = *b++;
+
+ return dest;
+}
+
+void art_svp_make_convex(ArtSVP *svp) {
+ int i;
+
+ if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
+ for (i = 0; i < svp->n_segs; i++) {
+ svp->segs[i].dir = !svp->segs[i].dir;
+ }
+ }
+}
+
+ArtVpath *art_vpath_reverse(ArtVpath *a) {
+ ArtVpath *dest;
+ ArtVpath it;
+ int len;
+ int state = 0;
+ int i;
+
+ len = art_vpath_len(a);
+ dest = art_new(ArtVpath, len + 1);
+
+ for (i = 0; i < len; i++) {
+ it = a[len - i - 1];
+ if (state) {
+ it.code = ART_LINETO;
+ } else {
+ it.code = ART_MOVETO_OPEN;
+ state = 1;
+ }
+ if (a[len - i - 1].code == ART_MOVETO ||
+ a[len - i - 1].code == ART_MOVETO_OPEN) {
+ state = 0;
+ }
+ dest[i] = it;
+ }
+ dest[len] = a[len];
+
+ return dest;
+}
+
+ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
+ ArtVpath *dest;
+
+ dest = art_vpath_reverse(a);
+ free(a);
+
+ return dest;
+}
+
+void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
+ ArtVpath *vec = NULL;
+ ArtVpath *vec1 = NULL;
+ ArtVpath *vec2 = NULL;
+ ArtSVP *svp = NULL;
+
+#if 0
+ const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
+ for (int i = 0;; i++) {
+ printf(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
+ if (bez[i].code == ART_END)
+ break;
+ if (bez[i].code == ART_CURVETO) {
+ printf(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
+ printf(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
+ }
+ printf(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
+ }
+
+ printf(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
+#endif
+
+ // HACK: Some frames have green bounding boxes drawn.
+ // Perhaps they were used by original game artist Umriss
+ // We skip them just like the original
+ if (bez2 == 0 && color == Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, 0x00, 0xff, 0x00)) {
+ return;
+ }
+
+ vec1 = art_bez_path_to_vec(bez1, 0.5);
+ if (bez2 != 0) {
+ vec2 = art_bez_path_to_vec(bez2, 0.5);
+ vec2 = art_vpath_reverse_free(vec2);
+ vec = art_vpath_cat(vec1, vec2);
+
+ free(vec1);
+ free(vec2);
+ } else {
+ vec = vec1;
+ }
+
+ int size = art_vpath_len(vec);
+ ArtVpath *vect = art_new(ArtVpath, size + 1);
+
+ int k;
+ for (k = 0; k < size; k++) {
+ vect[k].code = vec[k].code;
+ vect[k].x = (vec[k].x - deltaX) * scaleX;
+ vect[k].y = (vec[k].y - deltaY) * scaleY;
+ }
+ vect[k].code = ART_END;
+
+ if (bez2 == 0) { // Line drawing
+ svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
+ } else {
+ svp = art_svp_from_vpath(vect);
+ art_svp_make_convex(svp);
+ }
+
+ art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
+
+ free(vect);
+ free(svp);
+ free(vec);
+}
+
+void VectorImage::render(int width, int height) {
+ double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
+ double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
+
+ debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
+
+ if (_pixelData)
+ free(_pixelData);
+
+ _pixelData = (byte *)malloc(width * height * 4);
+ memset(_pixelData, 0, width * height * 4);
+
+ for (uint e = 0; e < _elements.size(); e++) {
+
+ //// Draw shapes
+ for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
+ int fill0len = 0;
+ int fill1len = 0;
+
+ // Count vector sizes in order to minimize memory
+ // fragmentation
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
+ fill0len += _elements[e].getPathInfo(p).getVecLen();
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
+ fill1len += _elements[e].getPathInfo(p).getVecLen();
+ }
+
+ // Now lump together vectors
+ ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
+ ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
+ ArtBpath *fill1pos = fill1;
+ ArtBpath *fill0pos = fill0;
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+ }
+
+ // Close vectors
+ (*fill0pos).code = ART_END;
+ (*fill1pos).code = ART_END;
+
+ drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
+
+ free(fill0);
+ free(fill1);
+ }
+
+ //// Draw strokes
+ for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
+ double penWidth = _elements[e].getLineStyleWidth(s);
+ penWidth *= sqrt(fabs(scaleX * scaleY));
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
+ drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
+ }
+ }
+ }
+ }
+}
+
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/panel.cpp b/engines/sword25/gfx/panel.cpp
new file mode 100644
index 0000000000..3aa0516835
--- /dev/null
+++ b/engines/sword25/gfx/panel.cpp
@@ -0,0 +1,135 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/panel.h"
+
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/image/image.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "PANEL"
+
+// -----------------------------------------------------------------------------
+// Construction/Destruction
+// -----------------------------------------------------------------------------
+
+Panel::Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color) :
+ RenderObject(parentPtr, RenderObject::TYPE_PANEL),
+ _color(color) {
+ _initSuccess = false;
+
+ _width = width;
+ _height = height;
+
+ if (_width < 0) {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid width (%d).", _width);
+ return;
+ }
+
+ if (_height < 0) {
+ BS_LOG_ERRORLN("Tried to initialise a panel with an invalid height (%d).", _height);
+ return;
+ }
+
+ _initSuccess = true;
+}
+
+// -----------------------------------------------------------------------------
+
+Panel::Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ RenderObject(parentPtr, RenderObject::TYPE_PANEL, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+// -----------------------------------------------------------------------------
+
+Panel::~Panel() {
+}
+
+// -----------------------------------------------------------------------------
+// Rendern
+// -----------------------------------------------------------------------------
+
+bool Panel::doRender() {
+ // Falls der Alphawert 0 ist, ist das Panel komplett durchsichtig und es muss nichts gezeichnet werden.
+ if (_color >> 24 == 0)
+ return true;
+
+ GraphicEngine *gfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(gfxPtr);
+
+ return gfxPtr->fill(&_bbox, _color);
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool Panel::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+ writer.write(_color);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool Panel::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ uint color;
+ reader.read(color);
+ setColor(color);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/panel.h b/engines/sword25/gfx/panel.h
new file mode 100644
index 0000000000..5fbcec5f34
--- /dev/null
+++ b/engines/sword25/gfx/panel.h
@@ -0,0 +1,81 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PANEL_H
+#define SWORD25_PANEL_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class Definition
+// -----------------------------------------------------------------------------
+
+class Panel : public RenderObject {
+ friend class RenderObject;
+
+private:
+ Panel(RenderObjectPtr<RenderObject> parentPtr, int width, int height, uint color);
+ Panel(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ virtual ~Panel();
+
+ uint getColor() const {
+ return _color;
+ }
+ void setColor(uint color) {
+ _color = color;
+ forceRefresh();
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ uint _color;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp
new file mode 100644
index 0000000000..5de6dde79e
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.cpp
@@ -0,0 +1,560 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobject.h"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/gfx/renderobjectregistry.h"
+#include "sword25/gfx/renderobjectmanager.h"
+#include "sword25/gfx/graphicengine.h"
+
+#include "sword25/gfx/bitmap.h"
+#include "sword25/gfx/staticbitmap.h"
+#include "sword25/gfx/dynamicbitmap.h"
+#include "sword25/gfx/animation.h"
+#include "sword25/gfx/panel.h"
+#include "sword25/gfx/text.h"
+#include "sword25/gfx/animationtemplate.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECT"
+
+// Konstruktion / Destruktion
+// --------------------------
+RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, uint handle) :
+ _managerPtr(0),
+ _parentPtr(parentPtr),
+ _x(0),
+ _y(0),
+ _z(0),
+ _oldX(-1),
+ _oldY(-1),
+ _oldZ(-1),
+ _width(0),
+ _height(0),
+ _visible(true),
+ _oldVisible(false),
+ _childChanged(true),
+ _type(type),
+ _initSuccess(false),
+ _refreshForced(true),
+ _handle(0) {
+
+ // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle.
+ if (handle == 0)
+ _handle = RenderObjectRegistry::getInstance().registerObject(this);
+ else
+ _handle = RenderObjectRegistry::getInstance().registerObject(this, handle);
+
+ if (_handle == 0)
+ return;
+
+ updateAbsolutePos();
+
+ // Dieses Objekt zu den Kindern der Elternobjektes hinzufügen, falls nicht Wurzel (ParentPtr ungültig) und dem
+ // selben RenderObjektManager zuweisen.
+ if (_parentPtr.isValid()) {
+ _managerPtr = _parentPtr->getManager();
+ _parentPtr->addObject(this->getHandle());
+ } else {
+ if (getType() != TYPE_ROOT) {
+ BS_LOG_ERRORLN("Tried to create a non-root render object and has no parent. All non-root render objects have to have a parent.");
+ return;
+ }
+ }
+
+ updateObjectState();
+
+ _initSuccess = true;
+}
+
+RenderObject::~RenderObject() {
+ // Objekt aus dem Elternobjekt entfernen.
+ if (_parentPtr.isValid())
+ _parentPtr->detatchChildren(this->getHandle());
+
+ deleteAllChildren();
+
+ // Objekt deregistrieren.
+ RenderObjectRegistry::getInstance().deregisterObject(this);
+}
+
+// Rendern
+// -------
+bool RenderObject::render() {
+ // Objektänderungen validieren
+ validateObject();
+
+ // Falls das Objekt nicht sichtbar ist, muss gar nichts gezeichnet werden
+ if (!_visible)
+ return true;
+
+ // Falls notwendig, wird die Renderreihenfolge der Kinderobjekte aktualisiert.
+ if (_childChanged) {
+ sortRenderObjects();
+ _childChanged = false;
+ }
+
+ // Objekt zeichnen.
+ doRender();
+
+ // Dann müssen die Kinder gezeichnet werden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->render())
+ return false;
+
+ return true;
+}
+
+// Objektverwaltung
+// ----------------
+
+void RenderObject::validateObject() {
+ // Die Veränderungen in den Objektvariablen aufheben
+ _oldBbox = _bbox;
+ _oldVisible = _visible;
+ _oldX = _x;
+ _oldY = _y;
+ _oldZ = _z;
+ _refreshForced = false;
+}
+
+bool RenderObject::updateObjectState() {
+ // Falls sich das Objekt verändert hat, muss der interne Zustand neu berechnet werden und evtl. Update-Regions für den nächsten Frame
+ // registriert werden.
+ if ((calcBoundingBox() != _oldBbox) ||
+ (_visible != _oldVisible) ||
+ (_x != _oldX) ||
+ (_y != _oldY) ||
+ (_z != _oldZ) ||
+ _refreshForced) {
+ // Renderrang des Objektes neu bestimmen, da sich dieser verändert haben könnte
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ // Die Bounding-Box neu berechnen und Update-Regions registrieren.
+ updateBoxes();
+
+ // Änderungen Validieren
+ validateObject();
+ }
+
+ // Dann muss der Objektstatus der Kinder aktualisiert werden.
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (!(*it)->updateObjectState())
+ return false;
+
+ return true;
+}
+
+void RenderObject::updateBoxes() {
+ // Bounding-Box aktualisieren
+ _bbox = calcBoundingBox();
+}
+
+Common::Rect RenderObject::calcBoundingBox() const {
+ // Die Bounding-Box mit der Objektgröße initialisieren.
+ Common::Rect bbox(0, 0, _width, _height);
+
+ // Die absolute Position der Bounding-Box berechnen.
+ bbox.translate(_absoluteX, _absoluteY);
+
+ // Die Bounding-Box am Elternobjekt clippen.
+ if (_parentPtr.isValid()) {
+ bbox.clip(_parentPtr->getBbox());
+ }
+
+ return bbox;
+}
+
+void RenderObject::calcAbsolutePos(int &x, int &y) const {
+ x = calcAbsoluteX();
+ y = calcAbsoluteY();
+}
+
+int RenderObject::calcAbsoluteX() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteX() + _x;
+ else
+ return _x;
+}
+
+int RenderObject::calcAbsoluteY() const {
+ if (_parentPtr.isValid())
+ return _parentPtr->getAbsoluteY() + _y;
+ else
+ return _y;
+}
+
+// Baumverwaltung
+// --------------
+
+void RenderObject::deleteAllChildren() {
+ // Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit
+ // selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist.
+ while (!_children.empty()) {
+ RenderObjectPtr<RenderObject> curPtr = _children.back();
+ curPtr.erase();
+ }
+}
+
+bool RenderObject::addObject(RenderObjectPtr<RenderObject> pObject) {
+ if (!pObject.isValid()) {
+ BS_LOG_ERRORLN("Tried to add a null object to a renderobject.");
+ return false;
+ }
+
+ // Objekt in die Kinderliste einfügen.
+ _children.push_back(pObject);
+
+ // Sicherstellen, dass vor dem nächsten Rendern die Renderreihenfolge aktualisiert wird.
+ if (_parentPtr.isValid())
+ _parentPtr->signalChildChange();
+
+ return true;
+}
+
+bool RenderObject::detatchChildren(RenderObjectPtr<RenderObject> pObject) {
+ // Kinderliste durchgehen und Objekt entfernen falls vorhanden
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ if (*it == pObject) {
+ _children.erase(it);
+ return true;
+ }
+
+ BS_LOG_ERRORLN("Tried to detach children from a render object that isn't its parent.");
+ return false;
+}
+
+void RenderObject::sortRenderObjects() {
+ Common::sort(_children.begin(), _children.end(), greater);
+}
+
+void RenderObject::updateAbsolutePos() {
+ calcAbsolutePos(_absoluteX, _absoluteY);
+
+ RENDEROBJECT_ITER it = _children.begin();
+ for (; it != _children.end(); ++it)
+ (*it)->updateAbsolutePos();
+}
+
+// Get-Methoden
+// ------------
+
+bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) {
+ result = pObject->getBbox();
+ result.clip(_bbox);
+ return result.isValidRect();
+}
+
+// Set-Methoden
+// ------------
+void RenderObject::setPos(int x, int y) {
+ _x = x;
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setX(int x) {
+ _x = x;
+ updateAbsolutePos();
+}
+
+void RenderObject::setY(int y) {
+ _y = y;
+ updateAbsolutePos();
+}
+
+void RenderObject::setZ(int z) {
+ if (z < 0)
+ BS_LOG_ERRORLN("Tried to set a negative Z value (%d).", z);
+ else
+ _z = z;
+}
+
+void RenderObject::setVisible(bool visible) {
+ _visible = visible;
+}
+
+// -----------------------------------------------------------------------------
+// Objekterzeuger
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const Common::String &filename) {
+ RenderObjectPtr<Animation> aniPtr((new Animation(this->getHandle(), filename))->getHandle());
+ if (aniPtr.isValid() && aniPtr->getInitSuccess())
+ return aniPtr;
+ else {
+ if (aniPtr.isValid())
+ aniPtr.erase();
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Animation> RenderObject::addAnimation(const AnimationTemplate &animationTemplate) {
+ Animation *aniPtr = new Animation(this->getHandle(), animationTemplate);
+ if (aniPtr && aniPtr->getInitSuccess())
+ return aniPtr->getHandle();
+ else {
+ delete aniPtr;
+ return RenderObjectPtr<Animation>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Bitmap> RenderObject::addBitmap(const Common::String &filename) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new StaticBitmap(this->getHandle(), filename))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return RenderObjectPtr<Bitmap>(bitmapPtr);
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Bitmap> RenderObject::addDynamicBitmap(uint width, uint height) {
+ RenderObjectPtr<Bitmap> bitmapPtr((new DynamicBitmap(this->getHandle(), width, height))->getHandle());
+ if (bitmapPtr.isValid() && bitmapPtr->getInitSuccess())
+ return bitmapPtr;
+ else {
+ if (bitmapPtr.isValid())
+ bitmapPtr.erase();
+ return RenderObjectPtr<Bitmap>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Panel> RenderObject::addPanel(int width, int height, uint color) {
+ RenderObjectPtr<Panel> panelPtr((new Panel(this->getHandle(), width, height, color))->getHandle());
+ if (panelPtr.isValid() && panelPtr->getInitSuccess())
+ return panelPtr;
+ else {
+ if (panelPtr.isValid())
+ panelPtr.erase();
+ return RenderObjectPtr<Panel>();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<Text> RenderObject::addText(const Common::String &font, const Common::String &text) {
+ RenderObjectPtr<Text> textPtr((new Text(this->getHandle()))->getHandle());
+ if (textPtr.isValid() && textPtr->getInitSuccess() && textPtr->SetFont(font)) {
+ textPtr->SetText(text);
+ return textPtr;
+ } else {
+ if (textPtr.isValid())
+ textPtr.erase();
+ return RenderObjectPtr<Text>();
+ }
+}
+
+// Persistenz-Methoden
+// -------------------
+
+bool RenderObject::persist(OutputPersistenceBlock &writer) {
+ // Typ und Handle werden als erstes gespeichert, damit beim Laden ein Objekt vom richtigen Typ mit dem richtigen Handle erzeugt werden kann.
+ writer.write(static_cast<uint>(_type));
+ writer.write(_handle);
+
+ // Restliche Objekteigenschaften speichern.
+ writer.write(_x);
+ writer.write(_y);
+ writer.write(_absoluteX);
+ writer.write(_absoluteY);
+ writer.write(_z);
+ writer.write(_width);
+ writer.write(_height);
+ writer.write(_visible);
+ writer.write(_childChanged);
+ writer.write(_initSuccess);
+ writer.write(_bbox.left);
+ writer.write(_bbox.top);
+ writer.write(_bbox.right);
+ writer.write(_bbox.bottom);
+ writer.write(_oldBbox.left);
+ writer.write(_oldBbox.top);
+ writer.write(_oldBbox.right);
+ writer.write(_oldBbox.bottom);
+ writer.write(_oldX);
+ writer.write(_oldY);
+ writer.write(_oldZ);
+ writer.write(_oldVisible);
+ writer.write(_parentPtr.isValid() ? _parentPtr->getHandle() : 0);
+ writer.write(_refreshForced);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::unpersist(InputPersistenceBlock &reader) {
+ // Typ und Handle wurden schon von RecreatePersistedRenderObject() ausgelesen. Jetzt werden die restlichen Objekteigenschaften ausgelesen.
+ reader.read(_x);
+ reader.read(_y);
+ reader.read(_absoluteX);
+ reader.read(_absoluteY);
+ reader.read(_z);
+ reader.read(_width);
+ reader.read(_height);
+ reader.read(_visible);
+ reader.read(_childChanged);
+ reader.read(_initSuccess);
+ reader.read(_bbox.left);
+ reader.read(_bbox.top);
+ reader.read(_bbox.right);
+ reader.read(_bbox.bottom);
+ reader.read(_oldBbox.left);
+ reader.read(_oldBbox.top);
+ reader.read(_oldBbox.right);
+ reader.read(_oldBbox.bottom);
+ reader.read(_oldX);
+ reader.read(_oldY);
+ reader.read(_oldZ);
+ reader.read(_oldVisible);
+ uint parentHandle;
+ reader.read(parentHandle);
+ _parentPtr = RenderObjectPtr<RenderObject>(parentHandle);
+ reader.read(_refreshForced);
+
+ updateAbsolutePos();
+ updateObjectState();
+
+ return reader.isGood();
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::persistChildren(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Kinderanzahl speichern.
+ writer.write(_children.size());
+
+ // Rekursiv alle Kinder speichern.
+ RENDEROBJECT_LIST::iterator it = _children.begin();
+ while (it != _children.end()) {
+ result &= (*it)->persist(writer);
+ ++it;
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool RenderObject::unpersistChildren(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Kinderanzahl einlesen.
+ uint childrenCount;
+ reader.read(childrenCount);
+ if (!reader.isGood())
+ return false;
+
+ // Alle Kinder rekursiv wieder herstellen.
+ for (uint i = 0; i < childrenCount; ++i) {
+ if (!recreatePersistedRenderObject(reader).isValid())
+ return false;
+ }
+
+ return result && reader.isGood();
+}
+
+// -----------------------------------------------------------------------------
+
+RenderObjectPtr<RenderObject> RenderObject::recreatePersistedRenderObject(InputPersistenceBlock &reader) {
+ RenderObjectPtr<RenderObject> result;
+
+ // Typ und Handle auslesen.
+ uint type;
+ uint handle;
+ reader.read(type);
+ reader.read(handle);
+ if (!reader.isGood())
+ return result;
+
+ switch (type) {
+ case TYPE_PANEL:
+ result = (new Panel(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_STATICBITMAP:
+ result = (new StaticBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_DYNAMICBITMAP:
+ result = (new DynamicBitmap(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_TEXT:
+ result = (new Text(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ case TYPE_ANIMATION:
+ result = (new Animation(reader, this->getHandle(), handle))->getHandle();
+ break;
+
+ default:
+ BS_LOG_ERRORLN("Cannot recreate render object of unknown type %d.", type);
+ }
+
+ return result;
+}
+
+// Hilfs-Methoden
+// --------------
+bool RenderObject::greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs) {
+ // Das Objekt mit dem kleinem Z-Wert müssen zuerst gerendert werden.
+ if (lhs->_z != rhs->_z)
+ return lhs->_z < rhs->_z;
+ // Falls der Z-Wert gleich ist, wird das weiter oben gelegenen Objekte zuerst gezeichnet.
+ return lhs->_y < rhs->_y;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobject.h b/engines/sword25/gfx/renderobject.h
new file mode 100644
index 0000000000..c090ad75c9
--- /dev/null
+++ b/engines/sword25/gfx/renderobject.h
@@ -0,0 +1,530 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_RenderObject
+ ---------------
+ Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt. Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjectManager in einem Baum verwaltet.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_RENDEROBJECT_H
+#define SWORD25_RENDEROBJECT_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobjectptr.h"
+
+#include "common/list.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class Kernel;
+class RenderObjectManager;
+class Bitmap;
+class Animation;
+class AnimationTemplate;
+class Panel;
+class Text;
+
+// Klassendefinition
+/**
+ @brief Dieses ist die Klasse die sämtliche sichtbaren Objekte beschreibt.
+
+ Alle anderen sichtbaren Objekte müssen von ihr abgeleitet werden.
+ Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
+ Alle BS_RenderObject Instanzen werden von einem BS_RenderObjektManager in einem Baum verwaltet.
+ */
+class RenderObject {
+public:
+ // Konstanten
+ // ----------
+ enum TYPES {
+ /// Das Wurzelobjekt. Siehe BS_RenderObjectManager
+ TYPE_ROOT,
+ /// Ein Image. Siehe BS_Bitmap.
+ TYPE_STATICBITMAP,
+ TYPE_DYNAMICBITMAP,
+ /// Eine Animation. Siehe BS_Animation.
+ TYPE_ANIMATION,
+ /// Eine farbige Fläche. Siehe BS_Panel.
+ TYPE_PANEL,
+ /// Ein Text. Siehe BS_Text.
+ TYPE_TEXT,
+ /// Ein unbekannter Objekttyp. Diesen Typ sollte kein Renderobjekt annehmen.
+ TYPE_UNKNOWN
+ };
+
+ // Add-Methoden
+ // ------------
+
+ /**
+ @brief Erzeugt ein Bitmap als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quellbilddatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> addBitmap(const Common::String &fileName);
+ /**
+ @brief Erzeugt ein veränderbares Bitmap als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Bitmaps
+ @param Height die Höhe des Bitmaps
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> addDynamicBitmap(uint width, uint height);
+ /**
+ @brief Erzeugt eine Animation auf Basis einer Animationsdatei als Kinderobjekt des Renderobjektes.
+ @param FileName der Dateiname der Quelldatei
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Animation> addAnimation(const Common::String &fileName);
+ /**
+ @brief Erzeugt eine Animation auf Basis eines Animationstemplate als Kinderobjekt des Renderobjektes.
+ @param pAnimationTemplate ein Pointer auf das Animationstemplate
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ @remark Das Renderobjekt übernimmt die Verwaltung des Animationstemplate.
+ */
+ RenderObjectPtr<Animation> addAnimation(const AnimationTemplate &animationTemplate);
+ /**
+ @brief Erzeugt ein neues Farbpanel als Kinderobjekt des Renderobjektes.
+ @param Width die Breite des Panels
+ @param Height die Höhe des Panels
+ @param Color die Farbe des Panels.<br>
+ Der Standardwert ist Schwarz (BS_RGB(0, 0, 0)).
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+
+ RenderObjectPtr<Panel> addPanel(int width, int height, uint color = 0xff000000);
+ /**
+ @brief Erzeugt ein Textobjekt als Kinderobjekt des Renderobjektes.
+ @param Font der Dateiname des zu verwendenen Fonts
+ @param Text der anzuzeigende Text.<br>
+ Der Standardwert ist "".
+ @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zurück.<br>
+ Falls ein Fehler aufgetreten ist wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Text> addText(const Common::String &font, const Common::String &text = "");
+
+ // Cast-Methoden
+ // -------------
+ /**
+ @brief Castet das Objekt zu einem BS_Bitmap-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Bitmap> toBitmap() {
+ if (_type == TYPE_STATICBITMAP || _type == TYPE_DYNAMICBITMAP)
+ return RenderObjectPtr<Bitmap>(this->getHandle());
+ else
+ return RenderObjectPtr<Bitmap>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Animation-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Animation> toAnimation() {
+ if (_type == TYPE_ANIMATION)
+ return RenderObjectPtr<Animation>(this->getHandle());
+ else
+ return RenderObjectPtr<Animation>();
+ }
+ /**
+ @brief Castet das Objekt zu einem BS_Panel-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Panel> toPanel() {
+ if (_type == TYPE_PANEL)
+ return RenderObjectPtr<Panel>(this->getHandle());
+ else
+ return RenderObjectPtr<Panel>();
+ }
+ /**
+ @brief Castet das Object zu einem BS_Text-Objekt wenn zulässig.
+ @return Gibt einen BS_RenderObjectPtr auf das Objekt zurück.<br>
+ Falls der Cast nicht zulässig ist, wird ein ungültiger BS_RenderObjectPtr zurückgegeben.
+ */
+ RenderObjectPtr<Text> toText() {
+ if (_type == TYPE_TEXT)
+ return RenderObjectPtr<Text>(this->getHandle());
+ else
+ return RenderObjectPtr<Text>();
+ }
+
+ // Konstruktor / Desktruktor
+ // -------------------------
+ /**
+ @brief Erzeugt ein neues BS_RenderObject.
+ @param pKernel ein Pointer auf den Kernel
+ @param pParent ein Pointer auf das Elternobjekt des neuen Objektes im Objektbaum.<br>
+ Der Pointer darf nicht NULL sein.
+ @param Type der Objekttyp<br>
+ Der Typ BS_RenderObject::TYPE_ROOT ist nicht zulässig. Wurzelknoten müssen mit dem alternativen Konstruktor erzeugt
+ werden.
+ @param Handle das Handle, welches dem Objekt zugewiesen werden soll.<br>
+ Dieser Parameter erzwingt ein bestimmtes Handle für das neue Objekt, oder wählt automatisch ein Handle, wenn der Parameter 0 ist.
+ Ist das gewünschte Handle bereits vergeben, gibt GetInitSuccess() false zurück.<br>
+ Der Standardwert ist 0.
+ @remark Nach dem Aufruf des Konstruktors kann über die Methode GetInitSuccess() abgefragt werden, ob die Konstruktion erfolgreich war.<br>
+ Es ist nicht notwendig alle BS_RenderObject Instanzen einzeln zu löschen. Dieses geschiet automatisch beim Löschen eines
+ Vorfahren oder beim Löschen des zuständigen BS_RenderObjectManager.
+ */
+ RenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
+ virtual ~RenderObject();
+
+ // Interface
+ // ---------
+ /**
+ @brief Rendert des Objekt und alle seine Unterobjekte.
+ @return Gibt false zurück, falls beim Rendern ein Fehler aufgetreten ist.
+ @remark Vor jedem Aufruf dieser Methode muss ein Aufruf von UpdateObjectState() erfolgt sein.
+ Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.<br>
+ Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool render();
+ /**
+ @brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor.
+ Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert.
+ @return Gibt false zurück, falls ein Fehler aufgetreten ist.
+ @remark Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
+ */
+ bool updateObjectState();
+ /**
+ @brief Löscht alle Kinderobjekte.
+ */
+ void deleteAllChildren();
+
+ // Accessor-Methoden
+ // -----------------
+ /**
+ @brief Setzt die Position des Objektes.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setPos(int x, int y);
+ /**
+ @brief Setzt die Position des Objektes auf der X-Achse.
+ @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setX(int x);
+ /**
+ @brief Setzt die Position des Objektes auf der Y-Achse.
+ @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
+ */
+ virtual void setY(int y);
+ /**
+ @brief Setzt den Z-Wert des Objektes.
+ @param Z der neue Z-Wert des Objektes relativ zum Elternobjekt<br>
+ Negative Z-Werte sind nicht zulässig.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ virtual void setZ(int z);
+ /**
+ @brief Setzt die Sichtbarkeit eine Objektes.
+ @param Visible der neue Sichtbarkeits-Zustand des Objektes<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ virtual void setVisible(bool visible);
+ /**
+ @brief Gibt die Position des Objektes auf der X-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int getX() const {
+ return _x;
+ }
+ /**
+ @brief Gibt die Position des Objektes auf der Y-Achse relativ zum Elternobjekt zurück.
+ */
+ virtual int getY() const {
+ return _y;
+ }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der X-Achse zurück.
+ */
+ virtual int getAbsoluteX() const {
+ return _absoluteX;
+ }
+ /**
+ @brief Gibt die absolute Position des Objektes auf der Y-Achse zurück.
+ */
+ virtual int getAbsoluteY() const {
+ return _absoluteY;
+ }
+ /**
+ @brief Gibt den Z-Wert des Objektes relativ zum Elternobjekt zurück.
+ @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit höherem
+ Z-Wert gezeichnet. Je höher der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
+ Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
+ Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
+ */
+ int getZ() const {
+ return _z;
+ }
+ /**
+ @brief Gibt die Breite des Objektes zurück.
+ */
+ int getWidth() const {
+ return _width;
+ }
+ /**
+ @brief Gibt die Höhe des Objektes zurück.
+ */
+ int getHeight() const {
+ return _height;
+ }
+ /**
+ @brief Gibt den Sichtbarkeitszustand des Objektes zurück.
+ @return Gibt den Sichtbarkeitszustand des Objektes zurück.<br>
+ true entspricht sichtbar, false entspricht unsichtbar.
+ */
+ bool isVisible() const {
+ return _visible;
+ }
+ /**
+ @brief Gibt den Typ des Objektes zurück.
+ */
+ TYPES getType() const {
+ return _type;
+ }
+ /**
+ @brief Gibt zurück, ob das Objekt erfolgreich initialisiert wurde.
+ @remark Hässlicher Workaround um das Problem, dass Konstruktoren keine Rückgabewerte haben.
+ */
+ bool getInitSuccess() const {
+ return _initSuccess;
+ }
+ /**
+ @brief Gibt die Bounding-Box des Objektes zurück.
+ @remark Diese Angabe erfolgt ausnahmsweise in Bildschirmkoordianten und nicht relativ zum Elternobjekt.
+ */
+ const Common::Rect &getBbox() const {
+ return _bbox;
+ }
+ /**
+ @brief Stellt sicher, dass das Objekt im nächsten Frame neu gezeichnet wird.
+ */
+ void forceRefresh() {
+ _refreshForced = true;
+ };
+ /**
+ @brief Gibt das Handle des Objekte zurück.
+ */
+ uint getHandle() const {
+ return _handle;
+ }
+
+ // Persistenz-Methoden
+ // -------------------
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+ // TODO: Evtl. protected
+ bool persistChildren(OutputPersistenceBlock &writer);
+ bool unpersistChildren(InputPersistenceBlock &reader);
+ // TODO: Evtl. private
+ RenderObjectPtr<RenderObject> recreatePersistedRenderObject(InputPersistenceBlock &reader);
+
+protected:
+ // Typen
+ // -----
+ typedef Common::List<RenderObjectPtr<RenderObject> > RENDEROBJECT_LIST;
+ typedef Common::List<RenderObjectPtr<RenderObject> >::iterator RENDEROBJECT_ITER;
+
+ int _x; ///< Die X-Position des Objektes relativ zum Eltern-Objekt
+ int _y; ///< Die Y-Position des Objektes relativ zum Eltern-Objekt
+ int _absoluteX; ///< Die absolute X-Position des Objektes
+ int _absoluteY; ///< Die absolute Y-Position des Objektes
+ int _z; ///< Der Z-Wert des Objektes relativ zum Eltern-Objekt
+ int _width; ///< Die Breite des Objektes
+ int _height; ///< Die Höhe des Objektes
+ bool _visible; ///< Ist true, wenn das Objekt sichtbar ist
+ bool _childChanged; ///< Ist true, wenn sich ein Kinderobjekt verändert hat
+ TYPES _type; ///< Der Objekttyp
+ bool _initSuccess; ///< Ist true, wenn Objekt erfolgreich intialisiert werden konnte
+ Common::Rect _bbox; ///< Die Bounding-Box des Objektes in Bildschirmkoordinaten
+
+ // Kopien der Variablen, die für die Errechnung des Dirty-Rects und zur Bestimmung der Objektveränderung notwendig sind
+ Common::Rect _oldBbox;
+ int _oldX;
+ int _oldY;
+ int _oldZ;
+ bool _oldVisible;
+
+ /// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet.
+ RenderObjectManager *_managerPtr;
+
+ // Render-Methode
+ // --------------
+ /**
+ @brief Einschubmethode, die den tatsächlichen Redervorgang durchführt.
+
+ Diese Methode wird von Render() aufgerufen um das Objekt darzustellen.
+ Diese Methode sollte von allen Klassen implementiert werden, die von BS_RederObject erben, um das Zeichnen umzusetzen.
+
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ @remark
+ */
+ virtual bool doRender() = 0; // { return true; };
+
+ // RenderObject-Baum Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Alle Eigenschaften wie X, Y, Z und Visible eines BS_RenderObjects sind relativ zu denen seines Vaters.
+ // Außerdem sind die Objekte von links nach rechts in ihrer Renderreihenfolge sortiert.
+ // Das primäre Sortierkriterium ist hierbei der Z-Wert und das sekundäre der Y-Wert (von oben nach unten).
+ // Beispiel:
+ // Screen
+ // / | \.
+ // / | \.
+ // / | \.
+ // / | \.
+ // Background Interface Maus
+ // / \ / | \.
+ // / \ / | \.
+ // George Tür Icn1 Icn2 Icn3
+ //
+ // Wenn jetzt das Interface mit SetVisible() ausgeblendet würde, verschwinden auch die Icons, die sich im Interface
+ // befinden.
+ // Wenn der Hintergrund bewegt wird (Scrolling), bewegen sich auch die darauf befindlichen Gegenstände und Personen.
+
+ /// Ein Pointer auf das Elternobjekt.
+ RenderObjectPtr<RenderObject> _parentPtr;
+ /// Die Liste der Kinderobjekte nach der Renderreihenfolge geordnet
+ RENDEROBJECT_LIST _children;
+
+ /**
+ @brief Gibt einen Pointer auf den BS_RenderObjektManager zurück, der das Objekt verwaltet.
+ */
+ RenderObjectManager *getManager() const {
+ return _managerPtr;
+ }
+ /**
+ @brief Fügt dem Objekt ein neues Kinderobjekt hinzu.
+ @param pObject ein Pointer auf das einzufügende Objekt
+ @return Gibt false zurück, falls das Objekt nicht eingefügt werden konnte.
+ */
+ bool addObject(RenderObjectPtr<RenderObject> pObject);
+
+private:
+ /// Ist true, wenn das Objekt in nächsten Frame neu gezeichnet werden soll
+ bool _refreshForced;
+
+ uint _handle;
+
+ /**
+ @brief Entfernt ein Objekt aus der Kinderliste.
+ @param pObject ein Pointer auf das zu entfernende Objekt
+ @return Gibt false zurück, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
+ */
+ bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
+ /**
+ @brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
+ */
+ void updateBoxes();
+ /**
+ @brief Berechnet die Bounding-Box des Objektes.
+ @return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zurück.
+ */
+ Common::Rect calcBoundingBox() const;
+ /**
+ @brief Berechnet das Dirty-Rectangle des Objektes.
+ @return Gibt das Dirty-Rectangle des Objektes in Bildschirmkoordinaten zurück.
+ */
+ Common::Rect calcDirtyRect() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ void calcAbsolutePos(int &x, int &y) const;
+ /**
+ @brief Berechnet die absolute Position des Objektes auf der X-Achse.
+ */
+ int calcAbsoluteX() const;
+ /**
+ @brief Berechnet die absolute Position des Objektes.
+ */
+ int calcAbsoluteY() const;
+ /**
+ @brief Sortiert alle Kinderobjekte nach ihrem Renderang.
+ */
+ void sortRenderObjects();
+ /**
+ @brief Validiert den Zustand eines Objektes nachdem die durch die Veränderung verursachten Folgen abgearbeitet wurden.
+ */
+ void validateObject();
+ /**
+ @brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.
+
+ Diese Methode muss aufgerufen werden, wann immer sich die Position des Objektes verändert. Damit die Kinderobjekte immer die
+ richtige absolute Position haben.
+ */
+ void updateAbsolutePos();
+ /**
+ @brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend verändert hat, die eine erneute Bestimmung der
+ Rendereihenfolge verlangt.
+ */
+ void signalChildChange() {
+ _childChanged = true;
+ }
+ /**
+ @brief Berechnet des Schnittrechteck der Bounding-Box des Objektes mit einem anderen Objekt.
+ @param pObjekt ein Pointer auf das Objekt mit dem geschnitten werden soll
+ @param Result das Ergebnisrechteck
+ @return Gibt false zurück, falls sich die Objekte gar nicht schneiden.
+ */
+ bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
+ /**
+ @brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
+ @remark Diese Methode wird fürs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
+ */
+ static bool greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectmanager.cpp b/engines/sword25/gfx/renderobjectmanager.cpp
new file mode 100644
index 0000000000..ab4e606270
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.cpp
@@ -0,0 +1,151 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobjectmanager.h"
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/gfx/animationtemplateregistry.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobject.h"
+#include "sword25/gfx/timedrenderobject.h"
+#include "sword25/gfx/rootrenderobject.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECTMANAGER"
+
+RenderObjectManager::RenderObjectManager(int width, int height, int framebufferCount) :
+ _frameStarted(false) {
+ // Wurzel des BS_RenderObject-Baumes erzeugen.
+ _rootPtr = (new RootRenderObject(this, width, height))->getHandle();
+}
+
+RenderObjectManager::~RenderObjectManager() {
+ // Die Wurzel des Baumes löschen, damit werden alle BS_RenderObjects mitgelöscht.
+ _rootPtr.erase();
+}
+
+void RenderObjectManager::startFrame() {
+ _frameStarted = true;
+
+ // Verstrichene Zeit bestimmen
+ int timeElapsed = Kernel::GetInstance()->GetGfx()->GetLastFrameDurationMicro();
+
+ // Alle BS_TimedRenderObject Objekte über den Framestart und die verstrichene Zeit in Kenntnis setzen
+ RenderObjectList::iterator iter = _timedRenderObjects.begin();
+ for (; iter != _timedRenderObjects.end(); ++iter)
+ (*iter)->frameNotification(timeElapsed);
+}
+
+bool RenderObjectManager::render() {
+ // Den Objekt-Status des Wurzelobjektes aktualisieren. Dadurch werden rekursiv alle Baumelemente aktualisiert.
+ // Beim aktualisieren des Objekt-Status werden auch die Update-Rects gefunden, so dass feststeht, was neu gezeichnet
+ // werden muss.
+ if (!_rootPtr.isValid() || !_rootPtr->updateObjectState())
+ return false;
+
+ _frameStarted = false;
+
+ // Die Render-Methode der Wurzel aufrufen. Dadurch wird das rekursive Rendern der Baumelemente angestoßen.
+ return _rootPtr->render();
+}
+
+void RenderObjectManager::attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
+ _timedRenderObjects.push_back(renderObjectPtr);
+}
+
+void RenderObjectManager::detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> renderObjectPtr) {
+ for (uint i = 0; i < _timedRenderObjects.size(); i++)
+ if (_timedRenderObjects[i] == renderObjectPtr) {
+ _timedRenderObjects.remove_at(i);
+ break;
+ }
+}
+
+bool RenderObjectManager::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Alle Kinder des Wurzelknotens speichern. Dadurch werden alle BS_RenderObjects gespeichert rekursiv gespeichert.
+ result &= _rootPtr->persistChildren(writer);
+
+ writer.write(_frameStarted);
+
+ // Referenzen auf die TimedRenderObjects persistieren.
+ writer.write(_timedRenderObjects.size());
+ RenderObjectList::const_iterator iter = _timedRenderObjects.begin();
+ while (iter != _timedRenderObjects.end()) {
+ writer.write((*iter)->getHandle());
+ ++iter;
+ }
+
+ // Alle BS_AnimationTemplates persistieren.
+ result &= AnimationTemplateRegistry::getInstance().persist(writer);
+
+ return result;
+}
+
+bool RenderObjectManager::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // Alle Kinder des Wurzelknotens löschen. Damit werden alle BS_RenderObjects gelöscht.
+ _rootPtr->deleteAllChildren();
+
+ // Alle BS_RenderObjects wieder hestellen.
+ if (!_rootPtr->unpersistChildren(reader))
+ return false;
+
+ reader.read(_frameStarted);
+
+ // Momentan gespeicherte Referenzen auf TimedRenderObjects löschen.
+ _timedRenderObjects.resize(0);
+
+ // Referenzen auf die TimedRenderObjects wieder herstellen.
+ uint timedObjectCount;
+ reader.read(timedObjectCount);
+ for (uint i = 0; i < timedObjectCount; ++i) {
+ uint handle;
+ reader.read(handle);
+ _timedRenderObjects.push_back(handle);
+ }
+
+ // Alle BS_AnimationTemplates wieder herstellen.
+ result &= AnimationTemplateRegistry::getInstance().unpersist(reader);
+
+ return result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobjectmanager.h b/engines/sword25/gfx/renderobjectmanager.h
new file mode 100644
index 0000000000..05bba37cd0
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectmanager.h
@@ -0,0 +1,131 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_RenderObjectManager
+ ----------------------
+ Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt z.B. dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden.
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_RENDEROBJECTMANAGER_H
+#define SWORD25_RENDEROBJECTMANAGER_H
+
+// Includes
+#include "common/rect.h"
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobjectptr.h"
+#include "sword25/kernel/persistable.h"
+
+namespace Sword25 {
+
+// Klassendefinition
+class Kernel;
+class RenderObject;
+class TimedRenderObject;
+
+/**
+ @brief Diese Klasse ist für die Verwaltung von BS_RenderObjects zuständig.
+
+ Sie sorgt dafür, dass die BS_RenderObjects in der richtigen Reihenfolge gerendert werden und ermöglicht den Zugriff auf die
+ BS_RenderObjects über einen String.
+*/
+class RenderObjectManager : public Persistable {
+public:
+ /**
+ @brief Erzeugt ein neues BS_RenderObjectManager-Objekt.
+ @param Width die horizontale Bildschirmauflösung in Pixeln
+ @param Height die vertikale Bildschirmauflösung in Pixeln
+ @param Die Anzahl an Framebuffern, die eingesetzt wird (Backbuffer + Primary).
+ */
+ RenderObjectManager(int width, int height, int framebufferCount);
+ virtual ~RenderObjectManager();
+
+ // Interface
+ // ---------
+ /**
+ @brief Initialisiert den Manager für einen neuen Frame.
+ @remark Alle Veränderungen an Objekten müssen nach einem Aufruf dieser Methode geschehen, damit sichergestellt ist, dass diese
+ visuell umgesetzt werden.<br>
+ Mit dem Aufruf dieser Methode werden die Rückgabewerte von GetUpdateRects() und GetUpdateRectCount() auf ihre Startwerte
+ zurückgesetzt. Wenn man also mit diesen Werten arbeiten möchten, muss man dies nach einem Aufruf von Render() und vor
+ einem Aufruf von StartFrame() tun.
+ */
+ void startFrame();
+ /**
+ @brief Rendert alle Objekte die sich während des letzten Aufrufes von Render() verändert haben.
+ @return Gibt false zurück, falls das Rendern fehlgeschlagen ist.
+ */
+ bool render();
+ /**
+ @brief Gibt einen Pointer auf die Wurzel des Objektbaumes zurück.
+ */
+ RenderObjectPtr<RenderObject> getTreeRoot() {
+ return _rootPtr;
+ }
+ /**
+ @brief Fügt ein BS_TimedRenderObject in die Liste der zeitabhängigen Render-Objekte.
+
+ Alle Objekte die sich in dieser Liste befinden werden vor jedem Frame über die seit dem letzten Frame
+ vergangene Zeit informiert, so dass sich ihren Zustand zeitabhängig verändern können.
+
+ @param RenderObject das einzufügende BS_TimedRenderObject
+ */
+ void attatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
+ /**
+ @brief Entfernt ein BS_TimedRenderObject aus der Liste für zeitabhängige Render-Objekte.
+ */
+ void detatchTimedRenderObject(RenderObjectPtr<TimedRenderObject> pRenderObject);
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool _frameStarted;
+ typedef Common::Array<RenderObjectPtr<TimedRenderObject> > RenderObjectList;
+ RenderObjectList _timedRenderObjects;
+
+ // RenderObject-Tree Variablen
+ // ---------------------------
+ // Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
+ // Zu weiteren Informationen siehe: "renderobject.h"
+ RenderObjectPtr<RenderObject> _rootPtr; // Die Wurzel der Baumes
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectptr.h b/engines/sword25/gfx/renderobjectptr.h
new file mode 100644
index 0000000000..894ba877d2
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectptr.h
@@ -0,0 +1,79 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDER_OBJECT_PTR_H
+#define SWORD25_RENDER_OBJECT_PTR_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobjectregistry.h"
+
+namespace Sword25 {
+
+class RenderObject;
+
+template<class T>
+class RenderObjectPtr {
+public:
+ RenderObjectPtr() : _handle(0) {}
+
+ RenderObjectPtr(uint handle) : _handle(handle) {}
+
+ T *operator->() const {
+ return static_cast<T *>(RenderObjectRegistry::getInstance().resolveHandle(_handle));
+ }
+
+ bool operator==(const RenderObjectPtr<T> & other) {
+ return _handle == other._handle;
+ }
+
+ bool isValid() const {
+ return RenderObjectRegistry::getInstance().resolveHandle(_handle) != 0;
+ }
+
+ void erase() {
+ delete static_cast<T *>(RenderObjectRegistry::getInstance().resolveHandle(_handle));
+ _handle = 0;
+ }
+
+private:
+ uint _handle;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/renderobjectregistry.cpp b/engines/sword25/gfx/renderobjectregistry.cpp
new file mode 100644
index 0000000000..edfdbd23a8
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.cpp
@@ -0,0 +1,53 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/renderobjectregistry.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RENDEROBJECTREGISTRY"
+
+Common::ScopedPtr<RenderObjectRegistry> RenderObjectRegistry::_instancePtr;
+
+void RenderObjectRegistry::logErrorLn(const char *message) const {
+ BS_LOG_ERRORLN(message);
+}
+
+void RenderObjectRegistry::logWarningLn(const char *message) const {
+ BS_LOG_WARNINGLN(message);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/renderobjectregistry.h b/engines/sword25/gfx/renderobjectregistry.h
new file mode 100644
index 0000000000..b546ee56e1
--- /dev/null
+++ b/engines/sword25/gfx/renderobjectregistry.h
@@ -0,0 +1,78 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RENDEROBJECTREGISTRY_H
+#define SWORD25_RENDEROBJECTREGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/objectregistry.h"
+
+#include "common/ptr.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward Deklarationen
+// -----------------------------------------------------------------------------
+
+class RenderObject;
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class RenderObjectRegistry : public ObjectRegistry<RenderObject> {
+public:
+ static RenderObjectRegistry &getInstance() {
+ if (!_instancePtr.get())
+ _instancePtr.reset(new RenderObjectRegistry);
+ return *_instancePtr.get();
+ }
+
+ virtual ~RenderObjectRegistry() {}
+
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+
+ static Common::ScopedPtr<RenderObjectRegistry> _instancePtr;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/rootrenderobject.h b/engines/sword25/gfx/rootrenderobject.h
new file mode 100644
index 0000000000..e4e3fba3c8
--- /dev/null
+++ b/engines/sword25/gfx/rootrenderobject.h
@@ -0,0 +1,72 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_ROOTRENDEROBJECT_H
+#define SWORD25_ROOTRENDEROBJECT_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class Kernel;
+
+// Klassendefinition
+class RenderObjectManager;
+
+class RootRenderObject : public RenderObject {
+ friend class RenderObjectManager;
+
+private:
+ RootRenderObject(RenderObjectManager *managerPtr, int width, int height) :
+ RenderObject(RenderObjectPtr<RenderObject>(), TYPE_ROOT) {
+ _managerPtr = managerPtr;
+ _width = width;
+ _height = height;
+ }
+
+protected:
+ virtual bool doRender() {
+ return true;
+ }
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/screenshot.cpp b/engines/sword25/gfx/screenshot.cpp
new file mode 100644
index 0000000000..9eea2ec422
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.cpp
@@ -0,0 +1,189 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "SCREENSHOT"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/system.h"
+#include "common/savefile.h"
+#include "sword25/gfx/screenshot.h"
+#include "sword25/kernel/filesystemutil.h"
+#include <png.h>
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+
+#include "common/pack-start.h"
+struct RGB_PIXEL {
+ byte Red;
+ byte Green;
+ byte Blue;
+} PACKED_STRUCT;
+#include "common/pack-end.h"
+
+void userWriteFn(png_structp png_ptr, png_bytep data, png_size_t length) {
+ static_cast<Common::WriteStream *>(png_ptr->io_ptr)->write(data, length);
+}
+
+void userFlushFn(png_structp png_ptr) {
+}
+
+
+bool Screenshot::SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream) {
+ // Reserve buffer space
+ RGB_PIXEL *pixelBuffer = new RGB_PIXEL[Data->w * Data->h];
+
+ // Convert the RGBA data to RGB
+ const byte *pSrc = (const byte *)Data->getBasePtr(0, 0);
+ RGB_PIXEL *pDest = pixelBuffer;
+
+ for (uint y = 0; y < Data->h; y++) {
+ for (uint x = 0; x < Data->w; x++) {
+ uint32 srcPixel = READ_LE_UINT32(pSrc);
+ pSrc += sizeof(uint32);
+ pDest->Red = (srcPixel >> 16) & 0xff;
+ pDest->Green = (srcPixel >> 8) & 0xff;
+ pDest->Blue = srcPixel & 0xff;
+ ++pDest;
+ }
+ }
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ error("Could not create PNG write-struct.");
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ error("Could not create PNG info-struct.");
+
+ // The compression buffer must be large enough to the entire image.
+ // This ensures that only an IDAT chunk is created.
+ // When buffer size is used 110% of the raw data size to be sure.
+ png_set_compression_buffer_size(png_ptr, (Data->w * Data->h * 3 * 110) / 100);
+
+ // Initialise PNG-Info structure
+ png_set_IHDR(png_ptr, info_ptr,
+ Data->w, // Width
+ Data->h, // Height
+ 8, // Bits depth
+ PNG_COLOR_TYPE_RGB, // Colour type
+ PNG_INTERLACE_NONE, // No interlacing
+ PNG_COMPRESSION_TYPE_DEFAULT, // Compression type
+ PNG_FILTER_TYPE_DEFAULT); // Filter Type
+
+ // Rowpointer erstellen
+ png_bytep *rowPointers = new png_bytep[Data->h];
+ for (uint i = 0; i < Data->h; i++) {
+ rowPointers[i] = (png_bytep)&pixelBuffer[Data->w * i];
+ }
+ png_set_rows(png_ptr, info_ptr, &rowPointers[0]);
+
+ // Write out the png data to the file
+ png_set_write_fn(png_ptr, (void *)Stream, userWriteFn, userFlushFn);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ delete[] pixelBuffer;
+ delete[] rowPointers;
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::MemoryReadStream *Screenshot::createThumbnail(Graphics::Surface *Data) {
+ // This method takes a screen image with a dimension of 800x600, and creates a screenshot with a dimension of 200x125.
+ // First 50 pixels are cut off the top and bottom (the interface boards in the game). The remaining image of 800x500
+ // will be on a 16th of its size, reduced by being handed out in 4x4 pixel blocks and the average of each block
+ // generates a pixel of the target image. Finally, the result as a PNG file is stored as a file.
+
+ // The source image must be 800x600.
+ if (Data->w != 800 || Data->h != 600 || Data->bytesPerPixel != 4) {
+ BS_LOG_ERRORLN("The sreenshot dimensions have to be 800x600 in order to be saved as a thumbnail.");
+ return false;
+ }
+
+ // Buffer for the output thumbnail
+ Graphics::Surface thumbnail;
+ thumbnail.create(200, 125, 4);
+
+ // Über das Zielbild iterieren und einen Pixel zur Zeit berechnen.
+ uint x, y;
+ x = y = 0;
+
+ for (byte *pDest = (byte *)thumbnail.pixels; pDest < ((byte *)thumbnail.pixels + thumbnail.pitch * thumbnail.h); ) {
+ // Get an average over a 4x4 pixel block in the source image
+ int alpha, red, green, blue;
+ alpha = red = green = blue = 0;
+ for (int j = 0; j < 4; ++j) {
+ const uint32 *srcP = (const uint32 *)Data->getBasePtr(x * 4, y * 4 + j + 50);
+ for (int i = 0; i < 4; ++i) {
+ uint32 pixel = READ_LE_UINT32(srcP + i);
+ alpha += (pixel >> 24);
+ red += (pixel >> 16) & 0xff;
+ green += (pixel >> 8) & 0xff;
+ blue += pixel & 0xff;
+ }
+ }
+
+ // Write target pixel
+ *pDest++ = blue / 16;
+ *pDest++ = green / 16;
+ *pDest++ = red / 16;
+ *pDest++ = alpha / 16;
+
+ // Move to next block
+ ++x;
+ if (x == thumbnail.w) {
+ x = 0;
+ ++y;
+ }
+ }
+
+ // Create a PNG representation of the thumbnail data
+ Common::MemoryWriteStreamDynamic *stream = new Common::MemoryWriteStreamDynamic();
+ SaveToFile(&thumbnail, stream);
+
+ // Output a MemoryReadStream that encompasses the written data
+ Common::MemoryReadStream *result = new Common::MemoryReadStream(stream->getData(), stream->size(),
+ DisposeAfterUse::YES);
+ return result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/screenshot.h b/engines/sword25/gfx/screenshot.h
new file mode 100644
index 0000000000..d328130b3f
--- /dev/null
+++ b/engines/sword25/gfx/screenshot.h
@@ -0,0 +1,59 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_SCREENSHOT_H
+#define SWORD25_SCREENSHOT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "graphics/surface.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class Screenshot {
+public:
+ static bool SaveToFile(Graphics::Surface *Data, Common::WriteStream *Stream);
+ static Common::MemoryReadStream *createThumbnail(Graphics::Surface *Data);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/staticbitmap.cpp b/engines/sword25/gfx/staticbitmap.cpp
new file mode 100644
index 0000000000..7771eb8100
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.cpp
@@ -0,0 +1,221 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/gfx/staticbitmap.h"
+#include "sword25/gfx/bitmapresource.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "STATICBITMAP"
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+StaticBitmap::StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename) :
+ Bitmap(parentPtr, TYPE_STATICBITMAP) {
+ // Das BS_Bitmap konnte nicht erzeugt werden, daher muss an dieser Stelle abgebrochen werden.
+ if (!_initSuccess)
+ return;
+
+ _initSuccess = initBitmapResource(filename);
+}
+
+// -----------------------------------------------------------------------------
+
+StaticBitmap::StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle) :
+ Bitmap(parentPtr, TYPE_STATICBITMAP, handle) {
+ _initSuccess = unpersist(reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::initBitmapResource(const Common::String &filename) {
+ // Bild-Resource laden
+ Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(filename);
+ if (!resourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", filename.c_str());
+ return false;
+ }
+ if (resourcePtr->GetType() != Resource::TYPE_BITMAP) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", filename.c_str());
+ return false;
+ }
+
+ BitmapResource *bitmapPtr = static_cast<BitmapResource *>(resourcePtr);
+
+ // Den eindeutigen Dateinamen zum späteren Referenzieren speichern
+ _resourceFilename = bitmapPtr->getFileName();
+
+ // RenderObject Eigenschaften aktualisieren
+ _originalWidth = _width = bitmapPtr->getWidth();
+ _originalHeight = _height = bitmapPtr->getHeight();
+
+ // Bild-Resource freigeben
+ bitmapPtr->release();
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+StaticBitmap::~StaticBitmap() {
+}
+
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::doRender() {
+ // Bitmap holen
+ Resource *resourcePtr = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ BS_ASSERT(resourcePtr);
+ BS_ASSERT(resourcePtr->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *bitmapResourcePtr = static_cast<BitmapResource *>(resourcePtr);
+
+ // Framebufferobjekt holen
+ GraphicEngine *gfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(gfxPtr);
+
+ // Bitmap zeichnen
+ bool result;
+ if (_scaleFactorX == 1.0f && _scaleFactorY == 1.0f) {
+ result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, -1, -1);
+ } else {
+ result = bitmapResourcePtr->blit(_absoluteX, _absoluteY,
+ (_flipV ? BitmapResource::FLIP_V : 0) |
+ (_flipH ? BitmapResource::FLIP_H : 0),
+ 0, _modulationColor, _width, _height);
+ }
+
+ // Resource freigeben
+ bitmapResourcePtr->release();
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+uint StaticBitmap::getPixel(int x, int y) const {
+ BS_ASSERT(x >= 0 && x < _width);
+ BS_ASSERT(y >= 0 && y < _height);
+
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ BitmapResource *pBitmapResource = static_cast<BitmapResource *>(pResource);
+ uint result = pBitmapResource->getPixel(x, y);
+ pResource->release();
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() ist not supported with this object.");
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+// Auskunftsmethoden
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::isAlphaAllowed() const {
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isAlphaAllowed();
+ pResource->release();
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::isColorModulationAllowed() const {
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isColorModulationAllowed();
+ pResource->release();
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::isScalingAllowed() const {
+ Resource *pResource = Kernel::GetInstance()->GetResourceManager()->RequestResource(_resourceFilename);
+ BS_ASSERT(pResource->GetType() == Resource::TYPE_BITMAP);
+ bool result = static_cast<BitmapResource *>(pResource)->isScalingAllowed();
+ pResource->release();
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool StaticBitmap::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= Bitmap::persist(writer);
+ writer.write(_resourceFilename);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool StaticBitmap::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= Bitmap::unpersist(reader);
+ Common::String resourceFilename;
+ reader.read(resourceFilename);
+ result &= initBitmapResource(resourceFilename);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/staticbitmap.h b/engines/sword25/gfx/staticbitmap.h
new file mode 100644
index 0000000000..a325487213
--- /dev/null
+++ b/engines/sword25/gfx/staticbitmap.h
@@ -0,0 +1,89 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_STATIC_BITMAP_H
+#define SWORD25_STATIC_BITMAP_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/bitmap.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+class StaticBitmap : public Bitmap {
+ friend class RenderObject;
+
+private:
+ /**
+ @remark Filename muss absoluter Pfad sein
+ */
+ StaticBitmap(RenderObjectPtr<RenderObject> parentPtr, const Common::String &filename);
+ StaticBitmap(InputPersistenceBlock &reader, RenderObjectPtr<RenderObject> parentPtr, uint handle);
+
+public:
+ virtual ~StaticBitmap();
+
+ virtual uint getPixel(int x, int y) const;
+
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+
+ virtual bool isScalingAllowed() const;
+ virtual bool isAlphaAllowed() const;
+ virtual bool isColorModulationAllowed() const;
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ Common::String _resourceFilename;
+
+ bool initBitmapResource(const Common::String &filename);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp
new file mode 100644
index 0000000000..1b0c3a78f0
--- /dev/null
+++ b/engines/sword25/gfx/text.cpp
@@ -0,0 +1,384 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// TODO:
+// Entweder Fontfile absolut abspeichern, oder Verzeichniswechseln verbieten
+// Eine relative Fontfile-Angabe könnte verwandt werden nachdem das Verzeichnis bereits gewechselt wurde und die Datei würde nicht mehr gefunden
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/gfx/fontresource.h"
+#include "sword25/gfx/bitmapresource.h"
+
+#include "sword25/gfx/text.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "TEXT"
+
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+namespace {
+const uint AUTO_WRAP_THRESHOLD_DEFAULT = 300;
+}
+
+// -----------------------------------------------------------------------------
+// Konstruktion / Destruktion
+// -----------------------------------------------------------------------------
+
+Text::Text(RenderObjectPtr<RenderObject> ParentPtr) :
+ RenderObject(ParentPtr, RenderObject::TYPE_TEXT),
+ _modulationColor(0xffffffff),
+ m_AutoWrap(false),
+ m_AutoWrapThreshold(AUTO_WRAP_THRESHOLD_DEFAULT) {
+
+}
+
+// -----------------------------------------------------------------------------
+
+Text::Text(InputPersistenceBlock &Reader, RenderObjectPtr<RenderObject> ParentPtr, uint Handle) :
+ RenderObject(ParentPtr, TYPE_TEXT, Handle) {
+ _initSuccess = unpersist(Reader);
+}
+
+// -----------------------------------------------------------------------------
+
+bool Text::SetFont(const Common::String &Font) {
+ // Font precachen.
+ if (GetResourceManager()->PrecacheResource(Font)) {
+ m_Font = Font;
+ UpdateFormat();
+ forceRefresh();
+ return true;
+ } else {
+ BS_LOG_ERRORLN("Could not precache font \"%s\". Font probably does not exist.", Font.c_str());
+ return false;
+ }
+
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::SetText(const Common::String &text) {
+ m_Text = text;
+ UpdateFormat();
+ forceRefresh();
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::setColor(uint modulationColor) {
+ uint newModulationColor = (modulationColor & 0x00ffffff) | (_modulationColor & 0xff000000);
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::setAlpha(int alpha) {
+ BS_ASSERT(alpha >= 0 && alpha < 256);
+ uint newModulationColor = (_modulationColor & 0x00ffffff) | alpha << 24;
+ if (newModulationColor != _modulationColor) {
+ _modulationColor = newModulationColor;
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::SetAutoWrap(bool AutoWrap) {
+ if (AutoWrap != m_AutoWrap) {
+ m_AutoWrap = AutoWrap;
+ UpdateFormat();
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::SetAutoWrapThreshold(uint AutoWrapThreshold) {
+ if (AutoWrapThreshold != m_AutoWrapThreshold) {
+ m_AutoWrapThreshold = AutoWrapThreshold;
+ UpdateFormat();
+ forceRefresh();
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool Text::doRender() {
+ // Font-Resource locken.
+ FontResource *FontPtr = LockFontResource();
+ if (!FontPtr) return false;
+
+ // Charactermap-Resource locken.
+ ResourceManager *RMPtr = GetResourceManager();
+ BitmapResource *CharMapPtr;
+ {
+ Resource *pResource = RMPtr->RequestResource(FontPtr->GetCharactermapFileName());
+ if (!pResource) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", FontPtr->GetCharactermapFileName().c_str());
+ return false;
+ }
+ if (pResource->GetType() != Resource::TYPE_BITMAP) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a bitmap.", FontPtr->GetCharactermapFileName().c_str());
+ return false;
+ }
+
+ CharMapPtr = static_cast<BitmapResource *>(pResource);
+ }
+
+ // Framebufferobjekt holen.
+ GraphicEngine *GfxPtr = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(GfxPtr);
+
+ bool Result = true;
+ Common::Array<LINE>::iterator Iter = m_Lines.begin();
+ for (; Iter != m_Lines.end(); ++Iter) {
+ // Feststellen, ob überhaupt Buchstaben der aktuellen Zeile vom Update betroffen sind.
+ Common::Rect CheckRect = (*Iter).BBox;
+ CheckRect.translate(_absoluteX, _absoluteY);
+
+ // Jeden Buchstaben einzeln Rendern.
+ int CurX = _absoluteX + (*Iter).BBox.left;
+ int CurY = _absoluteY + (*Iter).BBox.top;
+ for (uint i = 0; i < (*Iter).Text.size(); ++i) {
+ Common::Rect CurRect = FontPtr->GetCharacterRect((byte)(*Iter).Text[i]);
+
+ Common::Rect RenderRect(CurX, CurY, CurX + CurRect.width(), CurY + CurRect.height());
+ int RenderX = CurX + (RenderRect.left - RenderRect.left);
+ int RenderY = CurY + (RenderRect.top - RenderRect.top);
+ RenderRect.translate(CurRect.left - CurX, CurRect.top - CurY);
+ Result = CharMapPtr->blit(RenderX, RenderY, Image::FLIP_NONE, &RenderRect, _modulationColor);
+ if (!Result) break;
+
+ CurX += CurRect.width() + FontPtr->GetGapWidth();
+ }
+ }
+
+ // Charactermap-Resource freigeben.
+ CharMapPtr->release();
+
+ // Font-Resource freigeben.
+ FontPtr->release();
+
+ return Result;
+}
+
+// -----------------------------------------------------------------------------
+
+ResourceManager *Text::GetResourceManager() {
+ // Pointer auf den Resource-Manager holen.
+ return Kernel::GetInstance()->GetResourceManager();
+}
+
+// -----------------------------------------------------------------------------
+
+FontResource *Text::LockFontResource() {
+ ResourceManager *RMPtr = GetResourceManager();
+
+ // Font-Resource locken.
+ FontResource *FontPtr;
+ {
+ Resource *ResourcePtr = RMPtr->RequestResource(m_Font);
+ if (!ResourcePtr) {
+ BS_LOG_ERRORLN("Could not request resource \"%s\".", m_Font.c_str());
+ return NULL;
+ }
+ if (ResourcePtr->GetType() != Resource::TYPE_FONT) {
+ BS_LOG_ERRORLN("Requested resource \"%s\" is not a font.", m_Font.c_str());
+ return NULL;
+ }
+
+ FontPtr = static_cast<FontResource *>(ResourcePtr);
+ }
+
+ return FontPtr;
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::UpdateFormat() {
+ FontResource *FontPtr = LockFontResource();
+ BS_ASSERT(FontPtr);
+
+ UpdateMetrics(*FontPtr);
+
+ m_Lines.resize(1);
+ if (m_AutoWrap && (uint) _width >= m_AutoWrapThreshold && m_Text.size() >= 2) {
+ _width = 0;
+ uint CurLineWidth = 0;
+ uint CurLineHeight = 0;
+ uint CurLine = 0;
+ uint TempLineWidth = 0;
+ uint LastSpace = 0; // we need at least 1 space character to start a new line...
+ m_Lines[0].Text = "";
+ for (uint i = 0; i < m_Text.size(); ++i) {
+ uint j;
+ TempLineWidth = 0;
+ LastSpace = 0;
+ for (j = i; j < m_Text.size(); ++j) {
+ if ((byte)m_Text[j] == ' ') LastSpace = j;
+
+ const Common::Rect &CurCharRect = FontPtr->GetCharacterRect((byte)m_Text[j]);
+ TempLineWidth += CurCharRect.width();
+ TempLineWidth += FontPtr->GetGapWidth();
+
+ if ((TempLineWidth >= m_AutoWrapThreshold) && (LastSpace > 0))
+ break;
+ }
+
+ if (j == m_Text.size()) LastSpace = m_Text.size(); // everything in 1 line.
+
+ CurLineWidth = 0;
+ CurLineHeight = 0;
+ for (j = i; j < LastSpace; ++j) {
+ m_Lines[CurLine].Text += m_Text[j];
+
+ const Common::Rect &CurCharRect = FontPtr->GetCharacterRect((byte)m_Text[j]);
+ CurLineWidth += CurCharRect.width();
+ CurLineWidth += FontPtr->GetGapWidth();
+ if ((uint) CurCharRect.height() > CurLineHeight) CurLineHeight = CurCharRect.height();
+ }
+
+ m_Lines[CurLine].BBox.right = CurLineWidth;
+ m_Lines[CurLine].BBox.bottom = CurLineHeight;
+ if ((uint) _width < CurLineWidth) _width = CurLineWidth;
+
+ if (LastSpace < m_Text.size()) {
+ ++CurLine;
+ BS_ASSERT(CurLine == m_Lines.size());
+ m_Lines.resize(CurLine + 1);
+ m_Lines[CurLine].Text = "";
+ }
+
+ i = LastSpace;
+ }
+
+ // Bounding-Box der einzelnen Zeilen relativ zur ersten festlegen (vor allem zentrieren).
+ _height = 0;
+ Common::Array<LINE>::iterator Iter = m_Lines.begin();
+ for (; Iter != m_Lines.end(); ++Iter) {
+ Common::Rect &BBox = (*Iter).BBox;
+ BBox.left = (_width - BBox.right) / 2;
+ BBox.right = BBox.left + BBox.right;
+ BBox.top = (Iter - m_Lines.begin()) * FontPtr->GetLineHeight();
+ BBox.bottom = BBox.top + BBox.bottom;
+ _height += BBox.height();
+ }
+ } else {
+ // Keine automatische Formatierung, also wird der gesamte Text in nur eine Zeile kopiert.
+ m_Lines[0].Text = m_Text;
+ m_Lines[0].BBox = Common::Rect(0, 0, _width, _height);
+ }
+
+ FontPtr->release();
+}
+
+// -----------------------------------------------------------------------------
+
+void Text::UpdateMetrics(FontResource &FontResource) {
+ _width = 0;
+ _height = 0;
+
+ for (uint i = 0; i < m_Text.size(); ++i) {
+ const Common::Rect &CurRect = FontResource.GetCharacterRect((byte)m_Text[i]);
+ _width += CurRect.width();
+ if (i != m_Text.size() - 1) _width += FontResource.GetGapWidth();
+ if (_height < CurRect.height()) _height = CurRect.height();
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool Text::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ result &= RenderObject::persist(writer);
+
+ writer.write(_modulationColor);
+ writer.write(m_Font);
+ writer.write(m_Text);
+ writer.write(m_AutoWrap);
+ writer.write(m_AutoWrapThreshold);
+
+ result &= RenderObject::persistChildren(writer);
+
+ return result;
+}
+
+bool Text::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ result &= RenderObject::unpersist(reader);
+
+ // Farbe und Alpha einlesen.
+ reader.read(_modulationColor);
+
+ // Beim Laden der anderen Member werden die Set-Methoden benutzt statt der tatsächlichen Member.
+ // So wird das Layout automatisch aktualisiert und auch alle anderen notwendigen Methoden ausgeführt.
+
+ Common::String Font;
+ reader.read(Font);
+ SetFont(Font);
+
+ Common::String text;
+ reader.read(text);
+ SetText(text);
+
+ bool AutoWrap;
+ reader.read(AutoWrap);
+ SetAutoWrap(AutoWrap);
+
+ uint AutoWrapThreshold;
+ reader.read(AutoWrapThreshold);
+ SetAutoWrapThreshold(AutoWrapThreshold);
+
+ result &= RenderObject::unpersistChildren(reader);
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/text.h b/engines/sword25/gfx/text.h
new file mode 100644
index 0000000000..ed3a83e630
--- /dev/null
+++ b/engines/sword25/gfx/text.h
@@ -0,0 +1,181 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_TEXT_H
+#define SWORD25_TEXT_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "common/rect.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Forward Declarations
+// -----------------------------------------------------------------------------
+
+class Kernel;
+class FontResource;
+class ResourceManager;
+
+// -----------------------------------------------------------------------------
+// Klassendefinition
+// -----------------------------------------------------------------------------
+
+class Text : public RenderObject {
+ friend class RenderObject;
+
+public:
+ /**
+ @brief Setzt den Font mit dem der Text dargestellt werden soll.
+ @param Font der Dateiname der Fontdatei.
+ @return Gibt false zurück, wenn der Font nicht gefunden wurde.
+ */
+ bool SetFont(const Common::String &Font);
+
+ /**
+ @brief Setzt den darzustellenden Text.
+ @param Text der darzustellende Text
+ */
+ void SetText(const Common::String &text);
+
+ /**
+ @brief Setzt den Alphawert des Textes.
+ @param Alpha der neue Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ void setAlpha(int alpha);
+
+ /**
+ @brief Legt fest, ob der Text automatisch umgebrochen werden soll.
+
+ Wenn dieses Attribut auf true gesetzt ist, wird der Text umgebrochen, sofern er länger als GetAutoWrapThreshold() ist.
+
+ @param AutoWrap gibt an, ob der automatische Umbruch aktiviert oder deaktiviert werden soll.
+ @remark Dieses Attribut wird mit dem Wert false initialisiert.
+ */
+ void SetAutoWrap(bool AutoWrap);
+
+ /**
+ @brief Legt die Längengrenze des Textes in Pixeln fest, ab der ein automatischer Zeilenumbruch vorgenommen wird.
+ @remark Dieses Attribut wird mit dem Wert 300 initialisiert.
+ @remark Eine automatische Formatierung wird nur vorgenommen, wenn diese durch einen Aufruf von SetAutoWrap() aktiviert wurde.
+ */
+ void SetAutoWrapThreshold(uint AutoWrapThreshold);
+
+ /**
+ @brief Gibt den dargestellten Text zurück.
+ */
+ const Common::String &GetText() {
+ return m_Text;
+ }
+
+ /**
+ @brief Gibt den Namen das momentan benutzten Fonts zurück.
+ */
+ const Common::String &GetFont() {
+ return m_Font;
+ }
+
+ /**
+ @brief Setzt die Farbe des Textes.
+ @param Color eine 24-Bit RGB Farbe, die die Farbe des Textes festlegt.
+ */
+ void setColor(uint modulationColor);
+
+ /**
+ @brief Gibt den Alphawert des Textes zurück.
+ @return Der Alphawert des Textes (0 = keine Deckung, 255 = volle Deckung).
+ */
+ int getAlpha() const {
+ return _modulationColor >> 24;
+ }
+
+ /**
+ @brief Gibt die Farbe des Textes zurück.
+ @return Eine 24-Bit RGB Farbe, die die Farbe des Textes angibt.
+ */
+ int getColor() const {
+ return _modulationColor & 0x00ffffff;
+ }
+
+ /**
+ @brief Gibt zurück, ob die automatische Formatierung aktiviert ist.
+ */
+ bool IsAutoWrapActive() const {
+ return m_AutoWrap;
+ }
+
+ /**
+ @brief Gibt die Längengrenze des Textes in Pixeln zurück, ab der eine automatische Formatierung vorgenommen wird.
+ */
+ uint GetAutoWrapThreshold() const {
+ return m_AutoWrapThreshold;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ virtual bool doRender();
+
+private:
+ Text(RenderObjectPtr<RenderObject> ParentPtr);
+ Text(InputPersistenceBlock &Reader, RenderObjectPtr<RenderObject> ParentPtr, uint Handle);
+
+ uint _modulationColor;
+ Common::String m_Font;
+ Common::String m_Text;
+ bool m_AutoWrap;
+ uint m_AutoWrapThreshold;
+
+ struct LINE {
+ Common::Rect BBox;
+ Common::String Text;
+ };
+
+ Common::Array<LINE> m_Lines;
+
+ void UpdateFormat();
+ void UpdateMetrics(FontResource &FontResource);
+ ResourceManager *GetResourceManager();
+ FontResource *LockFontResource();
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/gfx/timedrenderobject.cpp b/engines/sword25/gfx/timedrenderobject.cpp
new file mode 100644
index 0000000000..eaa9b90d26
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.cpp
@@ -0,0 +1,52 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/gfx/timedrenderobject.h"
+
+#include "sword25/gfx/renderobjectmanager.h"
+
+namespace Sword25 {
+
+TimedRenderObject::TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle) :
+ RenderObject(pParent, type, handle) {
+ BS_ASSERT(getManager());
+ getManager()->attatchTimedRenderObject(this->getHandle());
+}
+
+TimedRenderObject::~TimedRenderObject() {
+ BS_ASSERT(getManager());
+ getManager()->detatchTimedRenderObject(this->getHandle());
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/gfx/timedrenderobject.h b/engines/sword25/gfx/timedrenderobject.h
new file mode 100644
index 0000000000..94d882d18e
--- /dev/null
+++ b/engines/sword25/gfx/timedrenderobject.h
@@ -0,0 +1,67 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// System Includes
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Engine Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/gfx/renderobject.h"
+
+namespace Sword25 {
+
+/**
+ @brief
+*/
+
+class TimedRenderObject : public RenderObject {
+public:
+ TimedRenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
+ ~TimedRenderObject();
+
+ /**
+ @brief Teilt dem Objekt mit, dass ein neuer Frame begonnen wird.
+
+ Diese Methode wird jeden Frame an jedem BS_TimedRenderObject aufgerufen um diesen zu ermöglichen
+ ihren Zustand Zeitabhängig zu verändern (z.B. Animationen).<br>
+ @param int TimeElapsed gibt an wie viel Zeit (in Microsekunden) seit dem letzten Frame vergangen ist.
+ */
+ virtual void frameNotification(int timeElapsed) = 0;
+};
+
+} // End of namespace Sword25
diff --git a/engines/sword25/input/inputengine.cpp b/engines/sword25/input/inputengine.cpp
new file mode 100644
index 0000000000..a57af23e6b
--- /dev/null
+++ b/engines/sword25/input/inputengine.cpp
@@ -0,0 +1,399 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "INPUTENGINE"
+
+#include "common/algorithm.h"
+#include "common/events.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/callbackregistry.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/input/inputengine.h"
+
+namespace Sword25 {
+
+#define DOUBLE_CLICK_TIME 500
+#define DOUBLE_CLICK_RECT_SIZE 4
+
+InputEngine::InputEngine(Kernel *pKernel) :
+ Service(pKernel),
+ m_CurrentState(0),
+ m_LeftMouseDown(false),
+ m_RightMouseDown(false),
+ m_MouseX(0),
+ m_MouseY(0),
+ m_LeftDoubleClick(false),
+ m_DoubleClickTime(DOUBLE_CLICK_TIME),
+ m_DoubleClickRectWidth(DOUBLE_CLICK_RECT_SIZE),
+ m_DoubleClickRectHeight(DOUBLE_CLICK_RECT_SIZE),
+ m_LastLeftClickTime(0),
+ m_LastLeftClickMouseX(0),
+ m_LastLeftClickMouseY(0) {
+ memset(m_KeyboardState[0], 0, sizeof(m_KeyboardState[0]));
+ memset(m_KeyboardState[1], 0, sizeof(m_KeyboardState[1]));
+ m_LeftMouseState[0] = false;
+ m_LeftMouseState[1] = false;
+ m_RightMouseState[0] = false;
+ m_RightMouseState[1] = false;
+
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+Service *InputEngine_CreateObject(Kernel *pKernel) {
+ return new InputEngine(pKernel);
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::Init() {
+ // No initialisation needed
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::Update() {
+ Common::Event event;
+ m_CurrentState ^= 1;
+
+ // Loop through processing any pending events
+ bool handleEvents = true;
+ while (handleEvents && g_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_LBUTTONUP:
+ m_LeftMouseDown = event.type == Common::EVENT_LBUTTONDOWN;
+ m_MouseX = event.mouse.x;
+ m_MouseY = event.mouse.y;
+ handleEvents = false;
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ case Common::EVENT_RBUTTONUP:
+ m_RightMouseDown = event.type == Common::EVENT_RBUTTONDOWN;
+ m_MouseX = event.mouse.x;
+ m_MouseY = event.mouse.y;
+ handleEvents = false;
+ break;
+
+ case Common::EVENT_MOUSEMOVE:
+ m_MouseX = event.mouse.x;
+ m_MouseY = event.mouse.y;
+ break;
+
+ case Common::EVENT_KEYDOWN:
+ case Common::EVENT_KEYUP:
+ AlterKeyboardState(event.kbd.keycode, (event.type == Common::EVENT_KEYDOWN) ? 0x80 : 0);
+ break;
+
+ case Common::EVENT_QUIT:
+ Kernel::GetInstance()->GetWindow()->SetWindowAlive(false);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ m_LeftMouseState[m_CurrentState] = m_LeftMouseDown;
+ m_RightMouseState[m_CurrentState] = m_RightMouseDown;
+
+ TestForLeftDoubleClick();
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::IsLeftMouseDown() {
+ return m_LeftMouseDown;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::IsRightMouseDown() {
+ return m_RightMouseDown;
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::TestForLeftDoubleClick() {
+ m_LeftDoubleClick = false;
+
+ // Only bother checking for a double click if the left mouse button was clicked
+ if (WasLeftMouseDown()) {
+ // Get the time now
+ uint Now = Kernel::GetInstance()->GetMilliTicks();
+
+ // A double click is signalled if
+ // 1. The two clicks are close enough together
+ // 2. The mouse cursor hasn't moved much
+ if (Now - m_LastLeftClickTime <= m_DoubleClickTime &&
+ ABS(m_MouseX - m_LastLeftClickMouseX) <= m_DoubleClickRectWidth / 2 &&
+ ABS(m_MouseY - m_LastLeftClickMouseY) <= m_DoubleClickRectHeight / 2) {
+ m_LeftDoubleClick = true;
+
+ // Reset the time and position of the last click, so that clicking is not
+ // interpreted as the first click of a further double-click
+ m_LastLeftClickTime = 0;
+ m_LastLeftClickMouseX = 0;
+ m_LastLeftClickMouseY = 0;
+ } else {
+ // There is no double click. Remember the position and time of the click,
+ // in case it's the first click of a double-click sequence
+ m_LastLeftClickTime = Now;
+ m_LastLeftClickMouseX = m_MouseX;
+ m_LastLeftClickMouseY = m_MouseY;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::AlterKeyboardState(int keycode, byte newState) {
+ m_KeyboardState[m_CurrentState][keycode] = newState;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::IsLeftDoubleClick() {
+ return m_LeftDoubleClick;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::WasLeftMouseDown() {
+ return (m_LeftMouseState[m_CurrentState] == false) && (m_LeftMouseState[m_CurrentState ^ 1] == true);
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::WasRightMouseDown() {
+ return (m_RightMouseState[m_CurrentState] == false) && (m_RightMouseState[m_CurrentState ^ 1] == true);
+}
+
+// -----------------------------------------------------------------------------
+
+int InputEngine::GetMouseX() {
+ return m_MouseX;
+}
+
+// -----------------------------------------------------------------------------
+
+int InputEngine::GetMouseY() {
+ return m_MouseY;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::IsKeyDown(uint KeyCode) {
+ return (m_KeyboardState[m_CurrentState][KeyCode] & 0x80) != 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::WasKeyDown(uint KeyCode) {
+ return ((m_KeyboardState[m_CurrentState][KeyCode] & 0x80) == 0) &&
+ ((m_KeyboardState[m_CurrentState ^ 1][KeyCode] & 0x80) != 0);
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::SetMouseX(int PosX) {
+ m_MouseX = PosX;
+ g_system->warpMouse(m_MouseX, m_MouseY);
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::SetMouseY(int PosY) {
+ m_MouseY = PosY;
+ g_system->warpMouse(m_MouseX, m_MouseY);
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::RegisterCharacterCallback(CharacterCallback Callback) {
+ if (Common::find(m_CharacterCallbacks.begin(), m_CharacterCallbacks.end(), Callback) == m_CharacterCallbacks.end()) {
+ m_CharacterCallbacks.push_back(Callback);
+ return true;
+ } else {
+ BS_LOG_WARNINGLN("Tried to register an CharacterCallback that was already registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::UnregisterCharacterCallback(CharacterCallback Callback) {
+ Common::List<CharacterCallback>::iterator CallbackIter = Common::find(m_CharacterCallbacks.begin(),
+ m_CharacterCallbacks.end(), Callback);
+ if (CallbackIter != m_CharacterCallbacks.end()) {
+ m_CharacterCallbacks.erase(CallbackIter);
+ return true;
+ } else {
+ BS_LOG_WARNINGLN("Tried to unregister an CharacterCallback that was not previously registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::RegisterCommandCallback(CommandCallback Callback) {
+ if (Common::find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback) == m_CommandCallbacks.end()) {
+ m_CommandCallbacks.push_back(Callback);
+ return true;
+ } else {
+ BS_LOG_WARNINGLN("Tried to register an CommandCallback that was already registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::UnregisterCommandCallback(CommandCallback Callback) {
+ Common::List<CommandCallback>::iterator CallbackIter =
+ Common::find(m_CommandCallbacks.begin(), m_CommandCallbacks.end(), Callback);
+ if (CallbackIter != m_CommandCallbacks.end()) {
+ m_CommandCallbacks.erase(CallbackIter);
+ return true;
+ } else {
+ BS_LOG_WARNINGLN("Tried to unregister an CommandCallback that was not previously registered.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::ReportCharacter(byte Character) {
+ Common::List<CharacterCallback>::const_iterator CallbackIter = m_CharacterCallbacks.begin();
+ while (CallbackIter != m_CharacterCallbacks.end()) {
+ // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
+ // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
+ // invalidiert wird.
+ Common::List<CharacterCallback>::const_iterator CurCallbackIter = CallbackIter;
+ ++CallbackIter;
+
+ (*CurCallbackIter)(Character);
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputEngine::ReportCommand(KEY_COMMANDS Command) {
+ Common::List<CommandCallback>::const_iterator CallbackIter = m_CommandCallbacks.begin();
+ while (CallbackIter != m_CommandCallbacks.end()) {
+ // Iterator vor dem Aufruf erhöhen und im Folgendem auf einer Kopie arbeiten.
+ // Dieses Vorgehen ist notwendig da der Iterator möglicherweise von der Callbackfunktion durch das Deregistrieren des Callbacks
+ // invalidiert wird.
+ Common::List<CommandCallback>::const_iterator CurCallbackIter = CallbackIter;
+ ++CallbackIter;
+
+ (*CurCallbackIter)(Command);
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Persistenz
+// -----------------------------------------------------------------------------
+
+bool InputEngine::persist(OutputPersistenceBlock &writer) {
+ // Anzahl an Command-Callbacks persistieren.
+ writer.write(m_CommandCallbacks.size());
+
+ // Alle Command-Callbacks einzeln persistieren.
+ {
+ Common::List<CommandCallback>::const_iterator It = m_CommandCallbacks.begin();
+ while (It != m_CommandCallbacks.end()) {
+ writer.write(CallbackRegistry::getInstance().resolveCallbackPointer(*It));
+ ++It;
+ }
+ }
+
+ // Anzahl an Character-Callbacks persistieren.
+ writer.write(m_CharacterCallbacks.size());
+
+ // Alle Character-Callbacks einzeln persistieren.
+ {
+ Common::List<CharacterCallback>::const_iterator It = m_CharacterCallbacks.begin();
+ while (It != m_CharacterCallbacks.end()) {
+ writer.write(CallbackRegistry::getInstance().resolveCallbackPointer(*It));
+ ++It;
+ }
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::unpersist(InputPersistenceBlock &reader) {
+ // Command-Callbackliste leeren.
+ m_CommandCallbacks.clear();
+
+ // Anzahl an Command-Callbacks lesen.
+ uint CommandCallbackCount;
+ reader.read(CommandCallbackCount);
+
+ // Alle Command-Callbacks wieder herstellen.
+ for (uint i = 0; i < CommandCallbackCount; ++i) {
+ Common::String CallbackFunctionName;
+ reader.read(CallbackFunctionName);
+
+ m_CommandCallbacks.push_back(reinterpret_cast<CommandCallback>(
+ CallbackRegistry::getInstance().resolveCallbackFunction(CallbackFunctionName)));
+ }
+
+ // Character-Callbackliste leeren.
+ m_CharacterCallbacks.clear();
+
+ // Anzahl an Character-Callbacks lesen.
+ uint CharacterCallbackCount;
+ reader.read(CharacterCallbackCount);
+
+ // Alle Character-Callbacks wieder herstellen.
+ for (uint i = 0; i < CharacterCallbackCount; ++i) {
+ Common::String CallbackFunctionName;
+ reader.read(CallbackFunctionName);
+
+ m_CharacterCallbacks.push_back(reinterpret_cast<CharacterCallback>(CallbackRegistry::getInstance().resolveCallbackFunction(CallbackFunctionName)));
+ }
+
+ return reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/input/inputengine.h b/engines/sword25/input/inputengine.h
new file mode 100644
index 0000000000..540817b5ce
--- /dev/null
+++ b/engines/sword25/input/inputengine.h
@@ -0,0 +1,333 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_InputEngine
+ * -------------
+ * This is the input interface engine that contains all the methods that an
+ * input source must implement.
+ * All input engines must be derived from this class.
+ *
+ * Autor: Alex Arnst
+ */
+
+#ifndef SWORD25_INPUTENGINE_H
+#define SWORD25_INPUTENGINE_H
+
+/// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/kernel/callbackregistry.h"
+
+namespace Sword25 {
+
+/// Class definitions
+
+class InputEngine : public Service, public Persistable {
+public:
+ InputEngine(Kernel *pKernel);
+ ~InputEngine() {};
+
+ // NOTE: These codes are registered in inputengine_script.cpp
+ // Any changes to these enums must also adjust the above file.
+ enum KEY_CODES {
+ KEY_BACKSPACE = 0x08,
+ KEY_TAB = 0x09,
+ KEY_CLEAR = 0x0C,
+ KEY_RETURN = 0x0D,
+ KEY_PAUSE = 0x13,
+ KEY_CAPSLOCK = 0x14,
+ KEY_ESCAPE = 0x1B,
+ KEY_SPACE = 0x20,
+ KEY_PAGEUP = 0x21,
+ KEY_PAGEDOWN = 0x22,
+ KEY_END = 0x23,
+ KEY_HOME = 0x24,
+ KEY_LEFT = 0x25,
+ KEY_UP = 0x26,
+ KEY_RIGHT = 0x27,
+ KEY_DOWN = 0x28,
+ KEY_PRINTSCREEN = 0x2C,
+ KEY_INSERT = 0x2D,
+ KEY_DELETE = 0x2E,
+ KEY_0 = 0x30,
+ KEY_1 = 0x31,
+ KEY_2 = 0x32,
+ KEY_3 = 0x33,
+ KEY_4 = 0x34,
+ KEY_5 = 0x35,
+ KEY_6 = 0x36,
+ KEY_7 = 0x37,
+ KEY_8 = 0x38,
+ KEY_9 = 0x39,
+ KEY_A = 0x41,
+ KEY_B = 0x42,
+ KEY_C = 0x43,
+ KEY_D = 0x44,
+ KEY_E = 0x45,
+ KEY_F = 0x46,
+ KEY_G = 0x47,
+ KEY_H = 0x48,
+ KEY_I = 0x49,
+ KEY_J = 0x4A,
+ KEY_K = 0x4B,
+ KEY_L = 0x4C,
+ KEY_M = 0x4D,
+ KEY_N = 0x4E,
+ KEY_O = 0x4F,
+ KEY_P = 0x50,
+ KEY_Q = 0x51,
+ KEY_R = 0x52,
+ KEY_S = 0x53,
+ KEY_T = 0x54,
+ KEY_U = 0x55,
+ KEY_V = 0x56,
+ KEY_W = 0x57,
+ KEY_X = 0x58,
+ KEY_Y = 0x59,
+ KEY_Z = 0x5A,
+ KEY_NUMPAD0 = 0x60,
+ KEY_NUMPAD1 = 0x61,
+ KEY_NUMPAD2 = 0x62,
+ KEY_NUMPAD3 = 0x63,
+ KEY_NUMPAD4 = 0x64,
+ KEY_NUMPAD5 = 0x65,
+ KEY_NUMPAD6 = 0x66,
+ KEY_NUMPAD7 = 0x67,
+ KEY_NUMPAD8 = 0x68,
+ KEY_NUMPAD9 = 0x69,
+ KEY_MULTIPLY = 0x6A,
+ KEY_ADD = 0x6B,
+ KEY_SEPARATOR = 0x6C,
+ KEY_SUBTRACT = 0x6D,
+ KEY_DECIMAL = 0x6E,
+ KEY_DIVIDE = 0x6F,
+ KEY_F1 = 0x70,
+ KEY_F2 = 0x71,
+ KEY_F3 = 0x72,
+ KEY_F4 = 0x73,
+ KEY_F5 = 0x74,
+ KEY_F6 = 0x75,
+ KEY_F7 = 0x76,
+ KEY_F8 = 0x77,
+ KEY_F9 = 0x78,
+ KEY_F10 = 0x79,
+ KEY_F11 = 0x7A,
+ KEY_F12 = 0x7B,
+ KEY_NUMLOCK = 0x90,
+ KEY_SCROLL = 0x91,
+ KEY_LSHIFT = 0xA0,
+ KEY_RSHIFT = 0xA1,
+ KEY_LCONTROL = 0xA2,
+ KEY_RCONTROL = 0xA3
+ };
+
+ // NOTE: These codes are registered in inputengine_script.cpp.
+ // Any changes to these enums must also adjust the above file.
+ enum KEY_COMMANDS {
+ KEY_COMMAND_ENTER = 1,
+ KEY_COMMAND_LEFT = 2,
+ KEY_COMMAND_RIGHT = 3,
+ KEY_COMMAND_HOME = 4,
+ KEY_COMMAND_END = 5,
+ KEY_COMMAND_BACKSPACE = 6,
+ KEY_COMMAND_TAB = 7,
+ KEY_COMMAND_INSERT = 8,
+ KEY_COMMAND_DELETE = 9
+ };
+
+ /// --------------------------------------------------------------
+ /// THESE METHODS MUST BE IMPLEMENTED BY THE INPUT ENGINE
+ /// --------------------------------------------------------------
+
+ /**
+ * Initialises the input engine
+ * @return Returns a true on success, otherwise false.
+ */
+ bool Init();
+
+ /**
+ * Performs a "tick" of the input engine.
+ *
+ * This method should be called once per frame. It can be used by implementations
+ * of the input engine that are not running in their own thread, or to perform
+ * additional administrative tasks that are needed.
+ */
+ void Update();
+
+ /**
+ * Returns true if the left mouse button is pressed
+ */
+ bool IsLeftMouseDown();
+
+ /**
+ * Returns true if the right mouse button is pressed.
+ */
+ bool IsRightMouseDown();
+
+ /**
+ * Returns true if the left mouse button was pressed and released.
+ *
+ * The difference between this and IsLeftMouseDown() is that this only returns
+ * true when the left mouse button is released.
+ */
+ bool WasLeftMouseDown();
+
+ /**
+ * Returns true if the right mouse button was pressed and released.
+ *
+ * The difference between this and IsRightMouseDown() is that this only returns
+ * true when the right mouse button is released.
+ */
+ bool WasRightMouseDown();
+
+ /**
+ * Returns true if the left mouse button double click was done
+ */
+ bool IsLeftDoubleClick();
+
+ /**
+ * Returns the X position of the cursor in pixels
+ */
+ int GetMouseX();
+
+ /**
+ * Returns the Y position of the cursor in pixels
+ */
+ int GetMouseY();
+
+ /**
+ * Sets the X position of the cursor in pixels
+ */
+ void SetMouseX(int PosX);
+
+ /**
+ * Sets the Y position of the cursor in pixels
+ */
+ void SetMouseY(int PosY);
+
+ /**
+ * Returns true if a given key was pressed
+ * @param KeyCode The key code to be checked
+ * @return Returns true if the given key is done, otherwise false.
+ */
+ bool IsKeyDown(uint KeyCode);
+
+ /**
+ * Returns true if a certain key was pushed and released.
+ *
+ * The difference between IsKeyDown() is that this only returns true after the key
+ * has been released. This method facilitates the retrieval of keys, and reading
+ * strings that users type.
+ * @param KeyCode The key code to be checked
+ */
+ bool WasKeyDown(uint KeyCode);
+
+ typedef CallbackPtr CharacterCallback;
+
+ /**
+ * Registers a callback function for keyboard input.
+ *
+ * The callbacks that are registered with this function will be called whenever an
+ * input key is pressed. A letter entry is different from the query using the
+ * methods IsKeyDown () and WasKeyDown () in the sense that are treated instead
+ * of actual scan-coded letters. These were taken into account, among other things:
+ * the keyboard layout, the condition the Shift and Caps Lock keys and the repetition
+ * of longer holding the key.
+ * The input of strings by the user through use of callbacks should be implemented.
+ * @return Returns true if the function was registered, otherwise false.
+ */
+ bool RegisterCharacterCallback(CallbackPtr Callback);
+
+ /**
+ * De-registeres a previously registered callback function.
+ * @return Returns true if the function could be de-registered, otherwise false.
+ */
+ bool UnregisterCharacterCallback(CallbackPtr Callback);
+
+ typedef CallbackPtr CommandCallback;
+
+ /**
+ * Registers a callback function for the input of commands that can have influence on the string input
+ *
+ * The callbacks that are registered with this function will be called whenever the input service
+ * has a key that affects the character string input. This could be the following keys:
+ * Enter, End, Left, Right, ...
+ * The input of strings by the user through the use of callbacks should be implemented.
+ * @return Returns true if the function was registered, otherwise false.
+ */
+ bool RegisterCommandCallback(CallbackPtr Callback);
+
+ /**
+ * Un-register a callback function for the input of commands that can have an influence on the string input.
+ * @return Returns true if the function could be de-registered, otherwise false.
+ */
+ bool UnregisterCommandCallback(CommandCallback Callback);
+
+ void ReportCharacter(byte Character);
+ void ReportCommand(KEY_COMMANDS Command);
+
+ bool persist(OutputPersistenceBlock &writer);
+ bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool registerScriptBindings();
+
+private:
+ void TestForLeftDoubleClick();
+ void AlterKeyboardState(int keycode, byte newState);
+
+ byte m_KeyboardState[2][256];
+ bool m_LeftMouseState[2];
+ bool m_RightMouseState[2];
+ uint m_CurrentState;
+ int m_MouseX;
+ int m_MouseY;
+ bool m_LeftMouseDown;
+ bool m_RightMouseDown;
+ bool m_LeftDoubleClick;
+ uint m_DoubleClickTime;
+ int m_DoubleClickRectWidth;
+ int m_DoubleClickRectHeight;
+ uint m_LastLeftClickTime;
+ int m_LastLeftClickMouseX;
+ int m_LastLeftClickMouseY;
+ Common::List<CommandCallback> m_CommandCallbacks;
+ Common::List<CharacterCallback> m_CharacterCallbacks;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/input/inputengine_script.cpp b/engines/sword25/input/inputengine_script.cpp
new file mode 100644
index 0000000000..38ecc3cf56
--- /dev/null
+++ b/engines/sword25/input/inputengine_script.cpp
@@ -0,0 +1,355 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/ptr.h"
+#include "common/str.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/callbackregistry.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+#include "sword25/script/luacallback.h"
+
+#include "sword25/input/inputengine.h"
+
+#define BS_LOG_PREFIX "INPUTENGINE"
+
+namespace Sword25 {
+
+using namespace Lua;
+
+// -----------------------------------------------------------------------------
+// Callback-Objekte
+// -----------------------------------------------------------------------------
+
+static void TheCharacterCallback(int Character);
+static void TheCommandCallback(int Command);
+
+namespace {
+class CharacterCallbackClass : public LuaCallback {
+public:
+ CharacterCallbackClass(lua_State *L) : LuaCallback(L) {};
+
+ Common::String Character;
+
+protected:
+ int PreFunctionInvokation(lua_State *L) {
+ lua_pushstring(L, Character.c_str());
+ return 1;
+ }
+};
+Common::SharedPtr<CharacterCallbackClass> CharacterCallbackPtr;
+
+// -----------------------------------------------------------------------------
+
+class CommandCallbackClass : public LuaCallback {
+public:
+ CommandCallbackClass(lua_State *L) : LuaCallback(L) {
+ Command = InputEngine::KEY_COMMAND_BACKSPACE;
+ }
+
+ InputEngine::KEY_COMMANDS Command;
+
+protected:
+ int PreFunctionInvokation(lua_State *L) {
+ lua_pushnumber(L, Command);
+ return 1;
+ }
+};
+Common::SharedPtr<CommandCallbackClass> CommandCallbackPtr;
+
+// -------------------------------------------------------------------------
+
+struct CallbackfunctionRegisterer {
+ CallbackfunctionRegisterer() {
+ CallbackRegistry::getInstance().registerCallbackFunction("LuaCommandCB", TheCommandCallback);
+ CallbackRegistry::getInstance().registerCallbackFunction("LuaCharacterCB", TheCharacterCallback);
+ }
+};
+static CallbackfunctionRegisterer Instance;
+}
+
+// -----------------------------------------------------------------------------
+
+static InputEngine *GetIE() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ InputEngine *pIE = static_cast<InputEngine *>(pKernel->GetService("input"));
+ BS_ASSERT(pIE);
+ return pIE;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Init(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->Init());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Update(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ // Beim ersten Aufruf der Update()-Methode werden die beiden Callbacks am Input-Objekt registriert.
+ // Dieses kann nicht in _RegisterScriptBindings() passieren, da diese Funktion vom Konstruktor der abstrakten Basisklasse aufgerufen wird und die
+ // Register...()-Methoden abstrakt sind, im Konstruktor der Basisklasse also nicht aufgerufen werden können.
+ static bool FirstCall = true;
+ if (FirstCall) {
+ FirstCall = false;
+ pIE->RegisterCharacterCallback(TheCharacterCallback);
+ pIE->RegisterCommandCallback(TheCommandCallback);
+ }
+
+ pIE->Update();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLeftMouseDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsLeftMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsRightMouseDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsRightMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasLeftMouseDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasLeftMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasRightMouseDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasRightMouseDown());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLeftDoubleClick(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsLeftDoubleClick());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMouseX(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushnumber(L, pIE->GetMouseX());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMouseY(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushnumber(L, pIE->GetMouseY());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsKeyDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->IsKeyDown((uint) luaL_checknumber(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WasKeyDown(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ lua_pushbooleancpp(L, pIE->WasKeyDown((uint) luaL_checknumber(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMouseX(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ pIE->SetMouseX((int) luaL_checknumber(L, 1));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMouseY(lua_State *L) {
+ InputEngine *pIE = GetIE();
+
+ pIE->SetMouseY((int) luaL_checknumber(L, 1));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void TheCharacterCallback(int Character) {
+ CharacterCallbackPtr->Character = static_cast<byte>(Character);
+ lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
+ CharacterCallbackPtr->invokeCallbackFunctions(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+static int RegisterCharacterCallback(lua_State *L) {
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CharacterCallbackPtr->registerCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int UnregisterCharacterCallback(lua_State *L) {
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CharacterCallbackPtr->unregisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static void TheCommandCallback(int Command) {
+ CommandCallbackPtr->Command = static_cast<InputEngine::KEY_COMMANDS>(Command);
+ lua_State *L = static_cast<lua_State *>(Kernel::GetInstance()->GetScript()->getScriptObject());
+ CommandCallbackPtr->invokeCallbackFunctions(L, 1);
+}
+
+// -----------------------------------------------------------------------------
+
+static int RegisterCommandCallback(lua_State *L) {
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CommandCallbackPtr->registerCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int UnregisterCommandCallback(lua_State *L) {
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ CommandCallbackPtr->unregisterCallbackFunction(L, 1);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char *PACKAGE_LIBRARY_NAME = "Input";
+
+static const luaL_reg PACKAGE_FUNCTIONS[] = {
+ {"Init", Init},
+ {"Update", Update},
+ {"IsLeftMouseDown", IsLeftMouseDown},
+ {"IsRightMouseDown", IsRightMouseDown},
+ {"WasLeftMouseDown", WasLeftMouseDown},
+ {"WasRightMouseDown", WasRightMouseDown},
+ {"IsLeftDoubleClick", IsLeftDoubleClick},
+ {"GetMouseX", GetMouseX},
+ {"GetMouseY", GetMouseY},
+ {"SetMouseX", SetMouseX},
+ {"SetMouseY", SetMouseY},
+ {"IsKeyDown", IsKeyDown},
+ {"WasKeyDown", WasKeyDown},
+ {"RegisterCharacterCallback", RegisterCharacterCallback},
+ {"UnregisterCharacterCallback", UnregisterCharacterCallback},
+ {"RegisterCommandCallback", RegisterCommandCallback},
+ {"UnregisterCommandCallback", UnregisterCommandCallback},
+ {0, 0}
+};
+
+#define X(k) {"KEY_" #k, InputEngine::KEY_##k}
+#define Y(k) {"KEY_COMMAND_" #k, InputEngine::KEY_COMMAND_##k}
+static const lua_constant_reg PACKAGE_CONSTANTS[] = {
+ X(BACKSPACE), X(TAB), X(CLEAR), X(RETURN), X(PAUSE), X(CAPSLOCK), X(ESCAPE), X(SPACE), X(PAGEUP), X(PAGEDOWN), X(END), X(HOME), X(LEFT),
+ X(UP), X(RIGHT), X(DOWN), X(PRINTSCREEN), X(INSERT), X(DELETE), X(0), X(1), X(2), X(3), X(4), X(5), X(6), X(7), X(8), X(9), X(A), X(B),
+ X(C), X(D), X(E), X(F), X(G), X(H), X(I), X(J), X(K), X(L), X(M), X(N), X(O), X(P), X(Q), X(R), X(S), X(T), X(U), X(V), X(W), X(X), X(Y),
+ X(Z), X(NUMPAD0), X(NUMPAD1), X(NUMPAD2), X(NUMPAD3), X(NUMPAD4), X(NUMPAD5), X(NUMPAD6), X(NUMPAD7), X(NUMPAD8), X(NUMPAD9), X(MULTIPLY),
+ X(ADD), X(SEPARATOR), X(SUBTRACT), X(DECIMAL), X(DIVIDE), X(F1), X(F2), X(F3), X(F4), X(F5), X(F6), X(F7), X(F8), X(F9), X(F10), X(F11),
+ X(F12), X(NUMLOCK), X(SCROLL), X(LSHIFT), X(RSHIFT), X(LCONTROL), X(RCONTROL),
+ Y(ENTER), Y(LEFT), Y(RIGHT), Y(HOME), Y(END), Y(BACKSPACE), Y(TAB), Y(INSERT), Y(DELETE),
+ {0, 0}
+};
+#undef X
+#undef Y
+
+// -----------------------------------------------------------------------------
+
+bool InputEngine::registerScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS)) return false;
+ if (!LuaBindhelper::addConstantsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_CONSTANTS)) return false;
+
+ CharacterCallbackPtr = Common::SharedPtr<CharacterCallbackClass>(new CharacterCallbackClass(L));
+ CommandCallbackPtr = Common::SharedPtr<CommandCallbackClass>(new CommandCallbackClass(L));
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/bs_stdint.h b/engines/sword25/kernel/bs_stdint.h
new file mode 100644
index 0000000000..c1970bff3e
--- /dev/null
+++ b/engines/sword25/kernel/bs_stdint.h
@@ -0,0 +1,54 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// TODO: Properly replace all game occurances that use these types with proper ScummVM types, and remove this file
+
+#ifndef SWORD25_STDINT_H
+#define SWORD25_STDINT_H
+
+#include "common/scummsys.h"
+
+typedef uint8 uint8_t;
+typedef uint16 uint16_t;
+typedef uint32 uint32_t;
+typedef int8 int8_t;
+typedef int16 int16_t;
+typedef int32 int32_t;
+
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+typedef unsigned long long uint64;
+typedef signed long long int64;
+
+#endif
diff --git a/engines/sword25/kernel/callbackregistry.cpp b/engines/sword25/kernel/callbackregistry.cpp
new file mode 100644
index 0000000000..32b2597334
--- /dev/null
+++ b/engines/sword25/kernel/callbackregistry.cpp
@@ -0,0 +1,131 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// Alle Callbackfunktionen die von Objekten gerufen werden, die persistiert werden können, müssen hier registriert werden.
+// Beim Speichern wird statt des Pointers der Bezeichner gespeichert. Beim Laden wird der Bezeichner wieder in einen Pointer umgewandelt.
+// Diese Klasse führt also so etwas ähnliches wie eine Importtabelle für Callback-Funktionen.
+//
+// Dieses Vorgehen hat mehrere Vorteile:
+// 1. Die Speicherstände sind plattformunabhängig. Es werden keine Pointer auf Funktionen gespeichert, sondern nur Namen von Callbackfunktionen.
+// Diese können beim Laden über diese Klasse in systemabhängige Pointer umgewandelt werden.
+// 2. Speicherstände können auch nach einem Engineupdate weiterhin benutzt werden. Beim Erstellen einer neun Binary verschieben sich häufig die
+// Funktionen. Eine Callbackfunktion könnte sich also nach einem Update an einer anderen Stelle befinden als davor. Wenn im Spielstand der
+// Pointer gespeichert war, stürtzt das Programm beim Äufrufen dieser Callbackfunktion ab. Durch das Auflösungverfahren wird beim Laden der
+// Callbackbezeichner in den neuen Funktionspointer umgewandelt und der Aufruf kann erfolgen.
+
+// -----------------------------------------------------------------------------
+// Logging
+// -----------------------------------------------------------------------------
+
+#define BS_LOG_PREFIX "CALLBACKREGISTRY"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/callbackregistry.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+
+bool CallbackRegistry::registerCallbackFunction(const Common::String &name, CallbackPtr ptr) {
+ if (name == "") {
+ BS_LOG_ERRORLN("The empty string is not allowed as a callback function name.");
+ return false;
+ }
+
+ if (findPtrByName(name) != 0) {
+ BS_LOG_ERRORLN("There is already a callback function with the name \"%s\".", name.c_str());
+ return false;
+ }
+ if (findNameByPtr(ptr) != "") {
+ BS_LOG_ERRORLN("There is already a callback function with the pointer 0x%x.", ptr);
+ return false;
+ }
+
+ storeCallbackFunction(name, ptr);
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+CallbackPtr CallbackRegistry::resolveCallbackFunction(const Common::String &name) const {
+ CallbackPtr result = findPtrByName(name);
+
+ if (!result) {
+ BS_LOG_ERRORLN("There is no callback function with the name \"%s\".", name.c_str());
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::String CallbackRegistry::resolveCallbackPointer(CallbackPtr ptr) const {
+ const Common::String &result = findNameByPtr(ptr);
+
+ if (result == "") {
+ BS_LOG_ERRORLN("There is no callback function with the pointer 0x%x.", ptr);
+ }
+
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+
+CallbackPtr CallbackRegistry::findPtrByName(const Common::String &name) const {
+ // Eintrag in der Map finden und den Pointer zurückgeben.
+ NameToPtrMap::const_iterator it = _nameToPtrMap.find(name);
+ return it == _nameToPtrMap.end() ? 0 : it->_value;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::String CallbackRegistry::findNameByPtr(CallbackPtr ptr) const {
+ // Eintrag in der Map finden und den Namen zurückgeben.
+ PtrToNameMap::const_iterator it = _ptrToNameMap.find(ptr);
+ return it == _ptrToNameMap.end() ? "" : it->_value;
+}
+
+// -----------------------------------------------------------------------------
+
+void CallbackRegistry::storeCallbackFunction(const Common::String &name, CallbackPtr ptr) {
+ // Callback-Funktion in beide Maps eintragen.
+ _nameToPtrMap[name] = ptr;
+ _ptrToNameMap[ptr] = name;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/callbackregistry.h b/engines/sword25/kernel/callbackregistry.h
new file mode 100644
index 0000000000..c5076d22f5
--- /dev/null
+++ b/engines/sword25/kernel/callbackregistry.h
@@ -0,0 +1,93 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_CALLBACK_REGISTRY_H
+#define SWORD25_CALLBACK_REGISTRY_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Klassendeklaration
+// -----------------------------------------------------------------------------
+
+typedef void (*CallbackPtr)(int command);
+
+class CallbackRegistry {
+public:
+ static CallbackRegistry &getInstance() {
+ static CallbackRegistry _instance;
+ return _instance;
+ }
+
+ bool registerCallbackFunction(const Common::String &name, CallbackPtr ptr);
+ CallbackPtr resolveCallbackFunction(const Common::String &name) const;
+ Common::String resolveCallbackPointer(CallbackPtr ptr) const;
+
+private:
+ typedef Common::HashMap<Common::String, CallbackPtr, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> NameToPtrMap;
+ NameToPtrMap _nameToPtrMap;
+
+ struct CallbackPtr_EqualTo {
+ bool operator()(CallbackPtr x, CallbackPtr y) const {
+ return x == y;
+ }
+ };
+ struct CallbackPtr_Hash {
+ uint operator()(CallbackPtr x) const {
+ return static_cast<uint>((int64)x % ((int64)1 << sizeof(uint)));
+ }
+ };
+
+ typedef Common::HashMap<CallbackPtr, Common::String, CallbackPtr_Hash, CallbackPtr_EqualTo> PtrToNameMap;
+ PtrToNameMap _ptrToNameMap;
+
+ CallbackPtr findPtrByName(const Common::String &name) const;
+ Common::String findNameByPtr(CallbackPtr ptr) const;
+ void storeCallbackFunction(const Common::String &name, CallbackPtr ptr);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/common.h b/engines/sword25/kernel/common.h
new file mode 100644
index 0000000000..7b11fe901f
--- /dev/null
+++ b/engines/sword25/kernel/common.h
@@ -0,0 +1,60 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * common.h
+ * -----------
+ * This file contains functions or macros that are used across the entire project.
+ * It is therefore extremely important that this header file be referenced in all
+ * the other header files in the project.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_COMMON_H
+#define SWORD25_COMMON_H
+
+// Global constants
+#define DEBUG
+
+#define BS_ACTIVATE_LOGGING // When defined, logging is activated
+
+// Engine Includes
+#include "sword25/kernel/log.h"
+
+#include "common/debug.h"
+
+#define BS_ASSERT(EXP) assert(EXP)
+
+#endif
diff --git a/engines/sword25/kernel/filesystemutil.cpp b/engines/sword25/kernel/filesystemutil.cpp
new file mode 100644
index 0000000000..853e6b247f
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil.cpp
@@ -0,0 +1,152 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/config-manager.h"
+#include "common/fs.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "sword25/kernel/filesystemutil.h"
+#include "sword25/kernel/persistenceservice.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "FILESYSTEMUTIL"
+
+// -----------------------------------------------------------------------------
+// Constants and utility functions
+// -----------------------------------------------------------------------------
+
+Common::String GetAbsolutePath(const Common::String &Path) {
+ Common::FSNode node(Path);
+
+ if (!node.exists()) {
+ // An error has occurred finding the node
+ // We can do nothing at this pointer than return an empty string
+ BS_LOG_ERRORLN("A call to GetAbsolutePath failed.");
+ return "";
+ }
+
+ // Return the result
+ return node.getPath();
+}
+
+// -----------------------------------------------------------------------------
+// Class definitions
+// -----------------------------------------------------------------------------
+
+class BS_FileSystemUtilScummVM : public FileSystemUtil {
+public:
+ virtual Common::String GetUserdataDirectory() {
+ Common::String path = ConfMan.get("savepath");
+
+ if (path.empty()) {
+ error("No save path has been defined");
+ return "";
+ }
+
+ // Return the path
+ return path;
+ }
+
+ virtual Common::String GetPathSeparator() {
+ return Common::String("/");
+ }
+
+ virtual int64 GetFileSize(const Common::String &Filename) {
+ Common::FSNode node(Filename);
+
+ // If the file does not exist, return -1 as a result
+ if (!node.exists())
+ return -1;
+
+ // Get the size of the file and return it
+ Common::File f;
+ f.open(node);
+ uint32 size = f.size();
+ f.close();
+
+ return size;
+ }
+
+ virtual TimeDate GetFileTime(const Common::String &Filename) {
+ // TODO: There isn't any way in ScummVM to get a file's modified date/time. We will need to check
+ // what code makes use of it. If it's only the save game code, for example, we may be able to
+ // encode the date/time inside the savegame files themselves.
+ TimeDate result;
+ g_system->getTimeAndDate(result);
+ return result;
+ }
+
+ virtual bool FileExists(const Common::String &Filename) {
+ Common::File f;
+ if (f.exists(Filename))
+ return true;
+
+ // Check if the file exists in the save folder
+ Common::FSNode folder(PersistenceService::GetSavegameDirectory());
+ Common::FSNode fileNode = folder.getChild(FileSystemUtil::GetInstance().GetPathFilename(Filename));
+ return fileNode.exists();
+ }
+
+ virtual bool CreateDirectory(const Common::String &DirectoryName) {
+ // ScummVM doesn't support creating folders, so this is only a stub
+ BS_LOG_ERRORLN("CreateDirectory method called");
+ return false;
+ }
+
+ virtual Common::String GetPathFilename(const Common::String &Path) {
+ for (int i = Path.size() - 1; i >= 0; --i) {
+ if ((Path[i] == '/') || (Path[i] == '\\')) {
+ return Common::String(&Path.c_str()[i + 1]);
+ }
+ }
+
+ return Path;
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Singleton method of parent class
+// -----------------------------------------------------------------------------
+
+FileSystemUtil &FileSystemUtil::GetInstance() {
+ static BS_FileSystemUtilScummVM Instance;
+ return Instance;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/filesystemutil.h b/engines/sword25/kernel/filesystemutil.h
new file mode 100644
index 0000000000..43ce7c908e
--- /dev/null
+++ b/engines/sword25/kernel/filesystemutil.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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ *
+ * The class BS_FileSystemUtil represents a wrapper for file system specific
+ * operations that do not have equivalents in the C/C++ libraries.
+ *
+ * Each supported platform must implement this interface, and the method
+ * BS_FileSystemUtil Singleton::getInstance()
+ */
+
+#ifndef SWORD25_FILESYSTEMUTIL_H
+#define SWORD25_FILESYSTEMUTIL_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/system.h"
+#include "common/str.h"
+#include "common/str-array.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/bs_stdint.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class definitions
+// -----------------------------------------------------------------------------
+
+class FileSystemUtil {
+public:
+ static FileSystemUtil &GetInstance();
+ virtual ~FileSystemUtil() {};
+
+ /**
+ * This function returns the name of the directory in which all user data is to be stored.
+ *
+ * These are for example Screenshots, game saves, configuration files, log files, ...
+ * @return Returns the name of the directory for user data.
+ */
+ virtual Common::String GetUserdataDirectory() = 0;
+ /**
+ * @return Returns the path seperator
+ */
+ virtual Common::String GetPathSeparator() = 0;
+ /**
+ * @param Filename The path to a file.
+ * @return Returns the size of the specified file. If the size could not be
+ * determined, or the file does not exist, returns -1
+ */
+ virtual int64 GetFileSize(const Common::String &Filename) = 0;
+ /**
+ * @param Filename The path to a file.
+ * @return Returns the timestamp of the specified file.
+ */
+ virtual TimeDate GetFileTime(const Common::String &Filename) = 0;
+ /**
+ * @param Filename The path to a file.
+ * @return Returns true if the file exists.
+ */
+ virtual bool FileExists(const Common::String &Filename) = 0;
+ /**
+ * This function creates a directory
+ *
+ * If the parameter is "\b\c\d\e" is passed, and "\b\c" already exists, then folder 'd'
+ * will be created, and subdirectory 'e' under it.
+ * @param DirectoryName The name of the directory to be created
+ * @return Returns true if the folder(s) could be created, otherwise false.
+ */
+ virtual bool CreateDirectory(const Common::String &DirectoryName) = 0;
+ /**
+ * Gets the filename from a path and filename
+ * @param Filename The full path and filename
+ * @return Returns just the filename
+ */
+ virtual Common::String GetPathFilename(const Common::String &Path) = 0;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/inputpersistenceblock.cpp b/engines/sword25/kernel/inputpersistenceblock.cpp
new file mode 100644
index 0000000000..b51b1037a7
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.cpp
@@ -0,0 +1,182 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "INPUTPERSISTENCEBLOCK"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Constructor / Destructor
+// -----------------------------------------------------------------------------
+
+InputPersistenceBlock::InputPersistenceBlock(const void *Data, uint DataLength) :
+ m_Data(static_cast<const byte *>(Data), DataLength),
+ m_ErrorState(NONE) {
+ m_Iter = m_Data.begin();
+}
+
+// -----------------------------------------------------------------------------
+
+InputPersistenceBlock::~InputPersistenceBlock() {
+ if (m_Iter != m_Data.end()) BS_LOG_WARNINGLN("Persistence block was not read to the end.");
+}
+
+// -----------------------------------------------------------------------------
+// Reading
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(int16 &Value) {
+ signed int v;
+ read(v);
+ Value = static_cast<int16>(v);
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(signed int &Value) {
+ if (CheckMarker(SINT_MARKER)) {
+ RawRead(&Value, sizeof(signed int));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ } else {
+ Value = 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(uint &Value) {
+ if (CheckMarker(UINT_MARKER)) {
+ RawRead(&Value, sizeof(uint));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ } else {
+ Value = 0;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(float &Value) {
+ if (CheckMarker(FLOAT_MARKER)) {
+ RawRead(&Value, sizeof(float));
+ Value = ConvertEndianessFromStorageToSystem(Value);
+ } else {
+ Value = 0.0f;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(bool &Value) {
+ if (CheckMarker(BOOL_MARKER)) {
+ uint UIntBool;
+ RawRead(&UIntBool, sizeof(float));
+ UIntBool = ConvertEndianessFromStorageToSystem(UIntBool);
+ Value = UIntBool == 0 ? false : true;
+ } else {
+ Value = 0.0f;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(Common::String &Value) {
+ Value = "";
+
+ if (CheckMarker(STRING_MARKER)) {
+ uint Size;
+ read(Size);
+
+ if (CheckBlockSize(Size)) {
+ Value = Common::String(reinterpret_cast<const char *>(&*m_Iter), Size);
+ m_Iter += Size;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::read(Common::Array<byte> &Value) {
+ if (CheckMarker(BLOCK_MARKER)) {
+ uint Size;
+ read(Size);
+
+ if (CheckBlockSize(Size)) {
+ Value = Common::Array<byte>(m_Iter, Size);
+ m_Iter += Size;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+void InputPersistenceBlock::RawRead(void *DestPtr, size_t Size) {
+ if (CheckBlockSize(Size)) {
+ memcpy(DestPtr, &*m_Iter, Size);
+ m_Iter += Size;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputPersistenceBlock::CheckBlockSize(int Size) {
+ if (m_Data.end() - m_Iter >= Size) {
+ return true;
+ } else {
+ m_ErrorState = END_OF_DATA;
+ BS_LOG_ERRORLN("Unexpected end of persistence block.");
+ return false;
+ }
+}
+
+// -----------------------------------------------------------------------------
+
+bool InputPersistenceBlock::CheckMarker(byte Marker) {
+ if (!isGood() || !CheckBlockSize(1)) return false;
+
+ if (*m_Iter++ == Marker) {
+ return true;
+ } else {
+ m_ErrorState = OUT_OF_SYNC;
+ BS_LOG_ERRORLN("Wrong type marker found in persistence block.");
+ return false;
+ }
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/inputpersistenceblock.h b/engines/sword25/kernel/inputpersistenceblock.h
new file mode 100644
index 0000000000..a6978e5899
--- /dev/null
+++ b/engines/sword25/kernel/inputpersistenceblock.h
@@ -0,0 +1,90 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_INPUTPERSISTENCEBLOCK_H
+#define SWORD25_INPUTPERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/array.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class InputPersistenceBlock : public PersistenceBlock {
+public:
+ enum ErrorState {
+ NONE,
+ END_OF_DATA,
+ OUT_OF_SYNC
+ };
+
+ InputPersistenceBlock(const void *Data, uint DataLength);
+ virtual ~InputPersistenceBlock();
+
+ void read(int16 &Value);
+ void read(signed int &Value);
+ void read(uint &Value);
+ void read(float &Value);
+ void read(bool &Value);
+ void read(Common::String &Value);
+ void read(Common::Array<byte> &Value);
+
+ bool isGood() const {
+ return m_ErrorState == NONE;
+ }
+ ErrorState GetErrorState() const {
+ return m_ErrorState;
+ }
+
+private:
+ bool CheckMarker(byte Marker);
+ bool CheckBlockSize(int Size);
+ void RawRead(void *DestPtr, size_t Size);
+
+ Common::Array<byte> m_Data;
+ Common::Array<byte>::const_iterator m_Iter;
+ ErrorState m_ErrorState;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/kernel.cpp b/engines/sword25/kernel/kernel.cpp
new file mode 100644
index 0000000000..3e7e7f125f
--- /dev/null
+++ b/engines/sword25/kernel/kernel.cpp
@@ -0,0 +1,454 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "common/system.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/fmv/movieplayer.h"
+#include "sword25/input/inputengine.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/persistenceservice.h"
+#include "sword25/kernel/service_ids.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/script/script.h"
+#include "sword25/sfx/soundengine.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "KERNEL"
+
+Kernel *Kernel::_Instance = 0;
+
+Kernel::Kernel() :
+ _pWindow(NULL),
+ _Running(false),
+ _pResourceManager(NULL),
+ _InitSuccess(false) {
+
+ // Log that the kernel is beign created
+ BS_LOGLN("created.");
+
+ // Read the BS_SERVICE_TABLE and prepare kernel structures
+ for (uint i = 0; i < BS_SERVICE_COUNT; i++) {
+ // Is the superclass already registered?
+ Superclass *pCurSuperclass = NULL;
+ Common::Array<Superclass *>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter)
+ if ((*Iter)->GetIdentifier() == BS_SERVICE_TABLE[i].SuperclassIdentifier) {
+ pCurSuperclass = *Iter;
+ break;
+ }
+
+ // If the superclass isn't already registered, then add it in
+ if (!pCurSuperclass)
+ _SuperclassList.push_back(new Superclass(this, BS_SERVICE_TABLE[i].SuperclassIdentifier));
+ }
+
+ // Create window object
+ _pWindow = Window::CreateBSWindow(0, 0, 0, 0, false);
+ if (!_pWindow) {
+ BS_LOG_ERRORLN("Failed to create the window.");
+ } else
+ BS_LOGLN("Window created.");
+
+ // Create the resource manager
+ _pResourceManager = new ResourceManager(this);
+
+ // Initialise the script engine
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(NewService("script", "lua"));
+ if (!pScript || !pScript->init()) {
+ _InitSuccess = false;
+ return;
+ }
+
+ // Register kernel script bindings
+ if (!_RegisterScriptBindings()) {
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ _InitSuccess = false;
+ return;
+ }
+ BS_LOGLN("Script bindings registered.");
+
+ _InitSuccess = true;
+}
+
+Kernel::~Kernel() {
+ // Services are de-registered in reverse order of creation
+ while (!_ServiceCreationOrder.empty()) {
+ Superclass *superclass = GetSuperclassByIdentifier(_ServiceCreationOrder.top());
+ if (superclass) superclass->DisconnectService();
+ _ServiceCreationOrder.pop();
+ }
+
+ // Empty the Superclass list
+ while (_SuperclassList.size()) {
+ delete _SuperclassList.back();
+ _SuperclassList.pop_back();
+ }
+
+ // Release the window object
+ delete _pWindow;
+ BS_LOGLN("Window destroyed.");
+
+ // Resource-Manager freigeben
+ delete _pResourceManager;
+
+ BS_LOGLN("destroyed.");
+}
+
+// Service Methoden
+// ----------------
+
+Kernel::Superclass::Superclass(Kernel *pKernel, const Common::String &Identifier) :
+ _pKernel(pKernel),
+ _Identifier(Identifier),
+ _ServiceCount(0),
+ _ActiveService(NULL) {
+ for (uint i = 0; i < BS_SERVICE_COUNT; i++)
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier)
+ _ServiceCount++;
+}
+
+Kernel::Superclass::~Superclass() {
+ DisconnectService();
+}
+
+/**
+ * Gets the identifier of a service with a given superclass.
+ * The number of services in a superclass can be learned with GetServiceCount().
+ * @param SuperclassIdentifier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
+ * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
+ * 0 und GetServiceCount() - 1 sein.
+ */
+Common::String Kernel::Superclass::GetServiceIdentifier(uint Number) {
+ if (Number > _ServiceCount) return NULL;
+
+ uint CurServiceOrd = 0;
+ for (uint i = 0; i < BS_SERVICE_COUNT; i++) {
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier) {
+ if (Number == CurServiceOrd)
+ return BS_SERVICE_TABLE[i].ServiceIdentifier;
+ else
+ CurServiceOrd++;
+ }
+ }
+
+ return Common::String("");
+}
+
+/**
+ * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
+ * service could not be created
+ * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
+ * @param SuperclassIdentifier The name of the superclass of the service
+ * z.B: "sfx", "gfx", "package" ...
+ * @param ServiceIdentifier The name of the service
+ * For the superclass "sfx" an example could be "Fmod" or "directsound"
+ */
+Service *Kernel::Superclass::NewService(const Common::String &ServiceIdentifier) {
+ for (uint i = 0; i < BS_SERVICE_COUNT; i++)
+ if (BS_SERVICE_TABLE[i].SuperclassIdentifier == _Identifier &&
+ BS_SERVICE_TABLE[i].ServiceIdentifier == ServiceIdentifier) {
+ Service *NewService_ = BS_SERVICE_TABLE[i].CreateMethod(_pKernel);
+
+ if (NewService_) {
+ DisconnectService();
+ BS_LOGLN("Service '%s' created from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ _ActiveService = NewService_;
+ _ActiveServiceName = BS_SERVICE_TABLE[i].ServiceIdentifier;
+ return _ActiveService;
+ } else {
+ BS_LOG_ERRORLN("Failed to create service '%s' from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ return NULL;
+ }
+ }
+
+ BS_LOG_ERRORLN("Service '%s' is not avaliable from superclass '%s'.", ServiceIdentifier.c_str(), _Identifier.c_str());
+ return NULL;
+}
+
+/**
+ * Ends the current service of a superclass. Returns true on success, and false if the superclass
+ * does not exist or if not service was active
+ * @param SuperclassIdentfier The name of the superclass which is to be disconnected
+ * z.B: "sfx", "gfx", "package" ...
+ */
+bool Kernel::Superclass::DisconnectService() {
+ if (_ActiveService) {
+ delete _ActiveService;
+ _ActiveService = 0;
+ BS_LOGLN("Active service '%s' disconnected from superclass '%s'.", _ActiveServiceName.c_str(), _Identifier.c_str());
+ return true;
+ }
+
+ return false;
+}
+
+Kernel::Superclass *Kernel::GetSuperclassByIdentifier(const Common::String &Identifier) {
+ Common::Array<Superclass *>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) {
+ if ((*Iter)->GetIdentifier() == Identifier)
+ return *Iter;
+ }
+
+ // BS_LOG_ERRORLN("Superclass '%s' does not exist.", Identifier.c_str());
+ return NULL;
+}
+
+/**
+ * Returns the number of register superclasses
+ */
+uint Kernel::GetSuperclassCount() {
+ return _SuperclassList.size();
+}
+
+/**
+ * Returns the name of a superclass with the specified index.
+ * Note: The number of superclasses can be retrieved using GetSuperclassCount
+ * @param Number The number of the superclass to return the identifier for.
+ * It should be noted that the number should be between 0 und GetSuperclassCount() - 1.
+ */
+Common::String Kernel::GetSuperclassIdentifier(uint Number) {
+ if (Number > _SuperclassList.size()) return NULL;
+
+ uint CurSuperclassOrd = 0;
+ Common::Array<Superclass *>::iterator Iter;
+ for (Iter = _SuperclassList.begin(); Iter != _SuperclassList.end(); ++Iter) {
+ if (CurSuperclassOrd == Number)
+ return ((*Iter)->GetIdentifier());
+
+ CurSuperclassOrd++;
+ }
+
+ return Common::String("");
+}
+
+/**
+ * Returns the number of services registered with a given superclass
+ * @param SuperclassIdentifier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+uint Kernel::GetServiceCount(const Common::String &SuperclassIdentifier) {
+ Superclass *pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier)))
+ return 0;
+
+ return pSuperclass->GetServiceCount();
+
+}
+
+/**
+ * Gets the identifier of a service with a given superclass.
+ * The number of services in a superclass can be learned with GetServiceCount().
+ * @param SuperclassIdentifier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
+ * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
+ * 0 und GetServiceCount() - 1 sein.
+ */
+Common::String Kernel::GetServiceIdentifier(const Common::String &SuperclassIdentifier, uint Number) {
+ Superclass *pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ return (pSuperclass->GetServiceIdentifier(Number));
+}
+
+/**
+ * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
+ * service could not be created
+ * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
+ * @param SuperclassIdentifier The name of the superclass of the service
+ * z.B: "sfx", "gfx", "package" ...
+ * @param ServiceIdentifier The name of the service
+ * For the superclass "sfx" an example could be "Fmod" or "directsound"
+ */
+Service *Kernel::NewService(const Common::String &SuperclassIdentifier, const Common::String &ServiceIdentifier) {
+ Superclass *pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ // Die Reihenfolge merken, in der Services erstellt werden, damit sie später in umgekehrter Reihenfolge entladen werden können.
+ _ServiceCreationOrder.push(SuperclassIdentifier);
+
+ return pSuperclass->NewService(ServiceIdentifier);
+}
+
+/**
+ * Ends the current service of a superclass. Returns true on success, and false if the superclass
+ * does not exist or if not service was active
+ * @param SuperclassIdentfier The name of the superclass which is to be disconnected
+ * z.B: "sfx", "gfx", "package" ...
+ */
+bool Kernel::DisconnectService(const Common::String &SuperclassIdentifier) {
+ Superclass *pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return false;
+
+ return pSuperclass->DisconnectService();
+}
+
+/**
+ * Returns a pointer to the currently active service object of a superclass
+ * @param SuperclassIdentfier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+Service *Kernel::GetService(const Common::String &SuperclassIdentifier) {
+ Superclass *pSuperclass;
+ if (!(pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier))) return NULL;
+
+ return (pSuperclass->GetActiveService());
+}
+
+/**
+ * Returns the name of the currentl active service object of a superclass.
+ * If an error occurs, then an empty string is returned
+ * @param SuperclassIdentfier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+Common::String Kernel::GetActiveServiceIdentifier(const Common::String &SuperclassIdentifier) {
+ Superclass *pSuperclass = GetSuperclassByIdentifier(SuperclassIdentifier);
+ if (!pSuperclass) return Common::String("");
+
+ return (pSuperclass->GetActiveServiceName());
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a random number
+ * @param Min The minimum allowed value
+ * @param Max The maximum allowed value
+ */
+int Kernel::GetRandomNumber(int Min, int Max) {
+ BS_ASSERT(Min <= Max);
+
+ return Min + _rnd.getRandomNumber(Max - Min + 1);
+}
+
+/**
+ * Returns the elapsed time since startup in milliseconds
+ */
+uint Kernel::GetMilliTicks() {
+ return g_system->getMillis();
+}
+
+/**
+ * Returns the elapsed time since the system start in microseconds.
+ * This method should be used only if GetMilliTick() for the desired application is inaccurate.
+ */
+uint64 Kernel::GetMicroTicks() {
+ return g_system->getMillis() * 1000;
+}
+
+// Other methods
+// -----------------
+
+/**
+ * Returns how much memory is being used
+ */
+size_t Kernel::GetUsedMemory() {
+ return 0;
+
+#ifdef SCUMMVM_DISABLED_CODE
+ PROCESS_MEMORY_COUNTERS pmc;
+ pmc.cb = sizeof(pmc);
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
+ return pmc.WorkingSetSize;
+ } else {
+ BS_LOG_ERRORLN("Call to GetProcessMemoryInfo() failed. Error code: %d", GetLastError());
+ return 0;
+ }
+#endif
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active
+ */
+GraphicEngine *Kernel::GetGfx() {
+ return static_cast<GraphicEngine *>(GetService("gfx"));
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active
+ */
+SoundEngine *Kernel::GetSfx() {
+ return static_cast<SoundEngine *>(GetService("sfx"));
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the active input service, or NULL if no input service is active
+ */
+InputEngine *Kernel::GetInput() {
+ return static_cast<InputEngine *>(GetService("input"));
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the active package manager, or NULL if no manager is active
+ */
+PackageManager *Kernel::GetPackage() {
+ return static_cast<PackageManager *>(GetService("package"));
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the script engine, or NULL if it is not active
+ */
+ScriptEngine *Kernel::GetScript() {
+ return static_cast<ScriptEngine *>(GetService("script"));
+}
+
+// -----------------------------------------------------------------------------
+
+/**
+ * Returns a pointer to the movie player, or NULL if it is not active
+ */
+MoviePlayer *Kernel::GetFMV() {
+ return static_cast<MoviePlayer *>(GetService("fmv"));
+}
+
+// -----------------------------------------------------------------------------
+
+void Kernel::Sleep(uint Msecs) const {
+ g_system->delayMillis(Msecs);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/kernel.h b/engines/sword25/kernel/kernel.h
new file mode 100644
index 0000000000..55a64c783f
--- /dev/null
+++ b/engines/sword25/kernel/kernel.h
@@ -0,0 +1,370 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_Kernel
+ * ---------
+ * This is the main class of the engine.
+ * This class creates and manages all other Engine elements: the sound engine, graphics engine ...
+ * It is not necessary to release all the items individually, this is performed by the Kernel class.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_KERNEL_H
+#define SWORD25_KERNEL_H
+
+// Includes
+#include "common/scummsys.h"
+#include "common/random.h"
+#include "common/stack.h"
+#include "common/util.h"
+#include "engines/engine.h"
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/kernel/window.h"
+#include "sword25/kernel/resmanager.h"
+
+namespace Sword25 {
+
+// Class definitions
+class Service;
+class GraphicEngine;
+class ScriptEngine;
+class SoundEngine;
+class InputEngine;
+class PackageManager;
+class MoviePlayer;
+
+/**
+ * This is the main engine class
+ *
+ * This class creates and manages all other engine components such as sound engine, graphics engine ...
+ * It is not necessary to release all the items individually, this is performed by the Kernel class.
+*/
+class Kernel {
+public:
+ // Window methods
+ // ----------------
+
+ /**
+ * Returns a pointer to the window object
+ */
+ Window *GetWindow() {
+ return _pWindow;
+ }
+
+ // Service Methods
+ // ---------------
+
+ /**
+ * Creates a new service with the given identifier. Returns a pointer to the service, or null if the
+ * service could not be created
+ * Note: All services must be registered in service_ids.h, otherwise they cannot be created here
+ * @param SuperclassIdentifier The name of the superclass of the service
+ * z.B: "sfx", "gfx", "package" ...
+ * @param ServiceIdentifier The name of the service
+ * For the superclass "sfx" an example could be "Fmod" or "directsound"
+ */
+ Service *NewService(const Common::String &SuperclassIdentifier, const Common::String &ServiceIdentifier);
+
+ /**
+ * Ends the current service of a superclass. Returns true on success, and false if the superclass
+ * does not exist or if not service was active
+ * @param SuperclassIdentfier The name of the superclass which is to be disconnected
+ * z.B: "sfx", "gfx", "package" ...
+ */
+ bool DisconnectService(const Common::String &SuperclassIdentifier);
+
+ /**
+ * Returns a pointer to the currently active service object of a superclass
+ * @param SuperclassIdentfier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+ Service *GetService(const Common::String &SuperclassIdentifier);
+
+ /**
+ * Returns the name of the currentl active service object of a superclass.
+ * If an error occurs, then an empty string is returned
+ * @param SuperclassIdentfier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+ Common::String GetActiveServiceIdentifier(const Common::String &SuperclassIdentifier);
+
+ /**
+ * Returns the number of register superclasses
+ */
+ uint GetSuperclassCount();
+
+ /**
+ * Returns the name of a superclass with the specified index.
+ * Note: The number of superclasses can be retrieved using GetSuperclassCount
+ * @param Number The number of the superclass to return the identifier for.
+ * It should be noted that the number should be between 0 und GetSuperclassCount() - 1.
+ */
+ Common::String GetSuperclassIdentifier(uint Number);
+
+ /**
+ * Returns the number of services registered with a given superclass
+ * @param SuperclassIdentifier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ */
+ uint GetServiceCount(const Common::String &SuperclassIdentifier);
+
+ /**
+ * Gets the identifier of a service with a given superclass.
+ * The number of services in a superclass can be learned with GetServiceCount().
+ * @param SuperclassIdentifier The name of the superclass
+ * z.B: "sfx", "gfx", "package" ...
+ * @param Number die Nummer des Services, dessen Bezeichner man erfahren will.<br>
+ * Hierbei ist zu beachten, dass der erste Service die Nummer 0 erhält. Number muss also eine Zahl zwischen
+ * 0 und GetServiceCount() - 1 sein.
+ */
+ Common::String GetServiceIdentifier(const Common::String &SuperclassIdentifier, uint Number);
+
+ /**
+ * Returns the elapsed time since startup in milliseconds
+ */
+ uint GetMilliTicks();
+
+ /**
+ * Returns the elapsed time since the system start in microseconds.
+ * This method should be used only if GetMilliTick() for the desired application is inaccurate.
+ */
+ uint64 GetMicroTicks();
+
+ /**
+ * Specifies whether the kernel was successfully initialised
+ */
+ bool GetInitSuccess() {
+ return _InitSuccess;
+ }
+ /**
+ * Returns a pointer to the BS_ResourceManager
+ */
+ ResourceManager *GetResourceManager() {
+ return _pResourceManager;
+ }
+ /**
+ * Returns how much memory is being used
+ */
+ size_t GetUsedMemory();
+ /**
+ * Returns a random number
+ * @param Min The minimum allowed value
+ * @param Max The maximum allowed value
+ */
+ int GetRandomNumber(int Min, int Max);
+ /**
+ * Returns a pointer to the active Gfx Service, or NULL if no Gfx service is active
+ */
+ GraphicEngine *GetGfx();
+ /**
+ * Returns a pointer to the active Sfx Service, or NULL if no Sfx service is active
+ */
+ SoundEngine *GetSfx();
+ /**
+ * Returns a pointer to the active input service, or NULL if no input service is active
+ */
+ InputEngine *GetInput();
+ /**
+ * Returns a pointer to the active package manager, or NULL if no manager is active
+ */
+ PackageManager *GetPackage();
+ /**
+ * Returns a pointer to the script engine, or NULL if it is not active
+ */
+ ScriptEngine *GetScript();
+ /**
+ * Returns a pointer to the movie player, or NULL if it is not active
+ */
+ MoviePlayer *GetFMV();
+
+ /**
+ * Pauses for the specified amount of time
+ * @param Msecs The amount of time in milliseconds
+ */
+ void Sleep(uint Msecs) const;
+
+ /**
+ * Returns the singleton instance for the kernel
+ */
+ static Kernel *GetInstance() {
+ if (!_Instance) _Instance = new Kernel();
+ return _Instance;
+ }
+
+ /**
+ * Destroys the kernel instance
+ * This method should only be called when the game is ended. No subsequent calls to any kernel
+ * methods should be done after calling this method.
+ */
+ static void DeleteInstance() {
+ if (_Instance) {
+ delete _Instance;
+ _Instance = NULL;
+ }
+ }
+
+ /**
+ * Raises an error. This method is used in crashing testing.
+ */
+ void Crash() const {
+ error("BS_Kernel::Crash");
+ }
+
+private:
+ // -----------------------------------------------------------------------------
+ // Constructor / destructor
+ // Private singleton methods
+ // -----------------------------------------------------------------------------
+
+ Kernel();
+ virtual ~Kernel();
+
+ // -----------------------------------------------------------------------------
+ // Singleton instance
+ // -----------------------------------------------------------------------------
+ static Kernel *_Instance;
+
+ // Superclass class
+ // ----------------
+ class Superclass {
+ private:
+ Kernel *_pKernel;
+ uint _ServiceCount;
+ Common::String _Identifier;
+ Service *_ActiveService;
+ Common::String _ActiveServiceName;
+
+ public:
+ Superclass(Kernel *pKernel, const Common::String &Identifier);
+ ~Superclass();
+
+ uint GetServiceCount() const {
+ return _ServiceCount;
+ }
+ Common::String GetIdentifier() const {
+ return _Identifier;
+ }
+ Service *GetActiveService() const {
+ return _ActiveService;
+ }
+ Common::String GetActiveServiceName() const {
+ return _ActiveServiceName;
+ }
+ Common::String GetServiceIdentifier(uint Number);
+ Service *NewService(const Common::String &ServiceIdentifier);
+ bool DisconnectService();
+ };
+
+ Common::Array<Superclass *> _SuperclassList;
+ Common::Stack<Common::String> _ServiceCreationOrder;
+ Superclass *GetSuperclassByIdentifier(const Common::String &Identifier);
+
+ bool _InitSuccess; // Specifies whether the engine was set up correctly
+ bool _Running; // Specifies whether the application should keep running on the next main loop iteration
+
+ // Active window
+ // -------------
+ Window *_pWindow;
+
+ // Random number generator
+ // -----------------------
+ Common::RandomSource _rnd;
+
+ /*
+ // Features variables and methods
+ // ----------------------------------
+ enum _CPU_FEATURES_BITMASKS
+ {
+ _MMX_BITMASK = (1 << 23),
+ _SSE_BITMASK = (1 << 25),
+ _SSE2_BITMASK = (1 << 26),
+ _3DNOW_BITMASK = (1 << 30),
+ _3DNOWEXT_BITMASK = (1 << 31)
+ };
+
+ bool _DetectCPU();
+
+ bool _MMXPresent;
+ bool _SSEPresent;
+ bool _SSE2Present;
+ bool _3DNowPresent;
+ bool _3DNowExtPresent;
+ CPU_TYPES _CPUType;
+ Common::String _CPUVendorID;
+ */
+
+ // Resourcemanager
+ // ---------------
+ ResourceManager *_pResourceManager;
+
+ bool _RegisterScriptBindings();
+};
+
+/**
+ * This is only a small class that manages the data of a service. It is a little ugly, I know,
+ * but with Common::String a simple struct could not be used.
+ */
+class BS_ServiceInfo {
+public:
+ BS_ServiceInfo(const Common::String &SuperclassIdentifier_, const Common::String &ServiceIdentifier_,
+ Service*(*CreateMethod_)(Kernel *)) {
+ this->SuperclassIdentifier = SuperclassIdentifier_;
+ this->ServiceIdentifier = ServiceIdentifier_;
+ this->CreateMethod = CreateMethod_;
+ };
+
+ Common::String SuperclassIdentifier;
+ Common::String ServiceIdentifier;
+ Service*(*CreateMethod)(Kernel *);
+};
+
+template<class T>
+void ReverseArray(Common::Array<T> &Arr) {
+ if (Arr.size() < 2)
+ return;
+
+ for (uint i = 0; i <= (Arr.size() / 2 - 1); ++i) {
+ T temp = Arr[i];
+ Arr[i] = Arr[Arr.size() - i - 1];
+ Arr[Arr.size() - i - 1] = temp;
+ }
+}
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/kernel_script.cpp b/engines/sword25/kernel/kernel_script.cpp
new file mode 100644
index 0000000000..1b87dfdc6e
--- /dev/null
+++ b/engines/sword25/kernel/kernel_script.cpp
@@ -0,0 +1,739 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/filesystemutil.h"
+#include "sword25/kernel/window.h"
+#include "sword25/kernel/resmanager.h"
+#include "sword25/kernel/persistenceservice.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+
+static int DisconnectService(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushboolean(L, pKernel->DisconnectService(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetActiveServiceIdentifier(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetActiveServiceIdentifier(luaL_checkstring(L, 1)).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSuperclassCount(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetSuperclassCount());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSuperclassIdentifier(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetSuperclassIdentifier(
+ static_cast<uint>(luaL_checknumber(L, 1))).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetServiceCount(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetServiceCount(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetServiceIdentifier(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushstring(L, pKernel->GetServiceIdentifier(luaL_checkstring(L, 1),
+ static_cast<uint>(luaL_checknumber(L, 2))).c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMilliTicks(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, pKernel->GetMilliTicks());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetTimer(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushnumber(L, static_cast<lua_Number>(pKernel->GetMicroTicks()) / 1000000.0);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int StartService(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+
+ lua_pushbooleancpp(L, pKernel->NewService(luaL_checkstring(L, 1), luaL_checkstring(L, 2)) != NULL);
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Sleep(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ pKernel->Sleep(static_cast<uint>(luaL_checknumber(L, 1) * 1000));
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int Crash(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ pKernel->Crash();
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ExecuteFile(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pSE = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pSE);
+
+ lua_pushbooleancpp(L, pSE->executeFile(luaL_checkstring(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetUserdataDirectory(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::GetInstance().GetUserdataDirectory().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetPathSeparator(lua_State *L) {
+ lua_pushstring(L, FileSystemUtil::GetInstance().GetPathSeparator().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int FileExists(lua_State *L) {
+ lua_pushbooleancpp(L, FileSystemUtil::GetInstance().FileExists(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int CreateDirectory(lua_State *L) {
+ lua_pushbooleancpp(L, FileSystemUtil::GetInstance().CreateDirectory(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetWinCode(lua_State *L) {
+ lua_pushstring(L, "ScummVM");
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSubversionRevision(lua_State *L) {
+ // ScummVM is 1337
+ lua_pushnumber(L, 1337);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetUsedMemory(lua_State *L) {
+ lua_pushnumber(L, Kernel::GetInstance()->GetUsedMemory());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char *KERNEL_LIBRARY_NAME = "Kernel";
+
+static const luaL_reg KERNEL_FUNCTIONS[] = {
+ {"DisconnectService", DisconnectService},
+ {"GetActiveServiceIdentifier", GetActiveServiceIdentifier},
+ {"GetSuperclassCount", GetSuperclassCount},
+ {"GetSuperclassIdentifier", GetSuperclassIdentifier},
+ {"GetServiceCount", GetServiceCount},
+ {"GetServiceIdentifier", GetServiceIdentifier},
+ {"GetMilliTicks", GetMilliTicks},
+ {"GetTimer", GetTimer},
+ {"StartService", StartService},
+ {"Sleep", Sleep},
+ {"Crash", Crash},
+ {"ExecuteFile", ExecuteFile},
+ {"GetUserdataDirectory", GetUserdataDirectory},
+ {"GetPathSeparator", GetPathSeparator},
+ {"FileExists", FileExists},
+ {"CreateDirectory", CreateDirectory},
+ {"GetWinCode", GetWinCode},
+ {"GetSubversionRevision", GetSubversionRevision},
+ {"GetUsedMemory", GetUsedMemory},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static int IsVisible(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->IsVisible());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetVisible(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetVisible(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetX(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetY(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetX(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetX(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetY(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetY(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetClientX(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetClientX());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetClientY(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetClientY());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetWidth(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetWidth());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetHeight(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushnumber(L, pWindow->GetHeight());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetWidth(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetWidth(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetHeight(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetHeight(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetTitle(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushstring(L, pWindow->GetTitle().c_str());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetTitle(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ pWindow->SetTitle(luaL_checkstring(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ProcessMessages(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->ProcessMessages());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int CloseWanted(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->CloseWanted());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int WaitForFocus(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->WaitForFocus());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int HasFocus(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ Window *pWindow = pKernel->GetWindow();
+ BS_ASSERT(pWindow);
+
+ lua_pushbooleancpp(L, pWindow->HasFocus());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char *WINDOW_LIBRARY_NAME = "Window";
+
+static const luaL_reg WINDOW_FUNCTIONS[] = {
+ {"IsVisible", IsVisible},
+ {"SetVisible", SetVisible},
+ {"GetX", GetX},
+ {"SetX", SetX},
+ {"GetY", GetY},
+ {"SetY", SetY},
+ {"GetClientX", GetClientX},
+ {"GetClientY", GetClientY},
+ {"GetWidth", GetWidth},
+ {"GetHeight", GetHeight},
+ {"SetWidth", SetWidth},
+ {"SetHeight", SetHeight},
+ {"GetTitle", GetTitle},
+ {"SetTitle", SetTitle},
+ {"ProcessMessages", ProcessMessages},
+ {"CloseWanted", CloseWanted},
+ {"WaitForFocus", WaitForFocus},
+ {"HasFocus", HasFocus},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static int PrecacheResource(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int ForcePrecacheResource(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->PrecacheResource(luaL_checkstring(L, 1), true));
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetMaxMemoryUsage(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushnumber(L, pResource->GetMaxMemoryUsage());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetMaxMemoryUsage(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->SetMaxMemoryUsage(static_cast<uint>(lua_tonumber(L, 1)));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int EmptyCache(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->EmptyCache();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsLogCacheMiss(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ lua_pushbooleancpp(L, pResource->IsLogCacheMiss());
+
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SetLogCacheMiss(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->SetLogCacheMiss(lua_tobooleancpp(L, 1));
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static int DumpLockedResources(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ BS_ASSERT(pResource);
+
+ pResource->DumpLockedResources();
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char *RESOURCE_LIBRARY_NAME = "Resource";
+
+static const luaL_reg RESOURCE_FUNCTIONS[] = {
+ {"PrecacheResource", PrecacheResource},
+ {"ForcePrecacheResource", ForcePrecacheResource},
+ {"GetMaxMemoryUsage", GetMaxMemoryUsage},
+ {"SetMaxMemoryUsage", SetMaxMemoryUsage},
+ {"EmptyCache", EmptyCache},
+ {"IsLogCacheMiss", IsLogCacheMiss},
+ {"SetLogCacheMiss", SetLogCacheMiss},
+ {"DumpLockedResources", DumpLockedResources},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+static int ReloadSlots(lua_State *L) {
+ PersistenceService::GetInstance().ReloadSlots();
+ lua_pushnil(L);
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSlotCount(lua_State *L) {
+ lua_pushnumber(L, PersistenceService::GetInstance().GetSlotCount());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSlotOccupied(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::GetInstance().IsSlotOccupied(
+ static_cast<uint>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameDirectory(lua_State *L) {
+ lua_pushstring(L, PersistenceService::GetInstance().GetSavegameDirectory().c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int IsSavegameCompatible(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::GetInstance().IsSavegameCompatible(
+ static_cast<uint>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameDescription(lua_State *L) {
+ lua_pushstring(L, PersistenceService::GetInstance().GetSavegameDescription(
+ static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int GetSavegameFilename(lua_State *L) {
+ lua_pushstring(L, PersistenceService::GetInstance().GetSavegameFilename(static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int LoadGame(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::GetInstance().LoadGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static int SaveGame(lua_State *L) {
+ lua_pushbooleancpp(L, PersistenceService::GetInstance().SaveGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
+ return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+static const char *PERSISTENCE_LIBRARY_NAME = "Persistence";
+
+static const luaL_reg PERSISTENCE_FUNCTIONS[] = {
+ {"ReloadSlots", ReloadSlots},
+ {"GetSlotCount", GetSlotCount},
+ {"IsSlotOccupied", IsSlotOccupied},
+ {"GetSavegameDirectory", GetSavegameDirectory},
+ {"IsSavegameCompatible", IsSavegameCompatible},
+ {"GetSavegameDescription", GetSavegameDescription},
+ {"GetSavegameFilename", GetSavegameFilename},
+ {"LoadGame", LoadGame},
+ {"SaveGame", SaveGame},
+ {0, 0}
+};
+
+// -----------------------------------------------------------------------------
+
+bool Kernel::_RegisterScriptBindings() {
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, KERNEL_LIBRARY_NAME, KERNEL_FUNCTIONS)) return false;
+ if (!LuaBindhelper::addFunctionsToLib(L, WINDOW_LIBRARY_NAME, WINDOW_FUNCTIONS)) return false;
+ if (!LuaBindhelper::addFunctionsToLib(L, RESOURCE_LIBRARY_NAME, RESOURCE_FUNCTIONS)) return false;
+ if (!LuaBindhelper::addFunctionsToLib(L, PERSISTENCE_LIBRARY_NAME, PERSISTENCE_FUNCTIONS)) return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/log.cpp b/engines/sword25/kernel/log.cpp
new file mode 100644
index 0000000000..259c02449f
--- /dev/null
+++ b/engines/sword25/kernel/log.cpp
@@ -0,0 +1,214 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/log.h"
+#include "base/version.h"
+#include "common/config-manager.h"
+#include "common/fs.h"
+
+namespace Sword25 {
+
+// Constants
+static const char *BF_LOG_FILENAME = "log.txt";
+static const size_t LOG_BUFFERSIZE = 1024 * 16;
+
+// Logging will take place only when it's activated
+#ifdef BS_ACTIVATE_LOGGING
+
+Common::WriteStream *BS_Log::_LogFile = NULL;
+bool BS_Log::_LineBegin = true;
+const char *BS_Log::_Prefix = NULL;
+const char *BS_Log::_File = NULL;
+int BS_Log::_Line = 0;
+bool BS_Log::_AutoNewline = false;
+Common::Array<BS_Log::LOG_LISTENER_CALLBACK> BS_Log::_LogListener;
+
+bool BS_Log::_CreateLog() {
+ // Open the log file
+ Common::FSNode dataDir(ConfMan.get("path"));
+ Common::FSNode file = dataDir.getChild(BF_LOG_FILENAME);
+
+ // Open the file for saving
+ _LogFile = file.createWriteStream();
+
+ if (_LogFile) {
+ // Add a title into the log file
+ Log("Broken Sword 2.5 Engine - Build: %s - %s - VersionID: %s\n", __DATE__, __TIME__, gScummVMFullVersion);
+ Log("-----------------------------------------------------------------------------------------------------\n");
+
+ return true;
+ }
+
+ // Log file could not be created
+ return false;
+}
+
+void BS_Log::_CloseLog() {
+ delete _LogFile;
+ _LogFile = NULL;
+}
+
+void BS_Log::Log(const char *Format, ...) {
+ char Message[LOG_BUFFERSIZE];
+
+ // Create the message
+ va_list ArgList;
+ va_start(ArgList, Format);
+ vsnprintf(Message, sizeof(Message), Format, ArgList);
+
+ // Log the message
+ _WriteLog(Message);
+
+ _FlushLog();
+}
+
+void BS_Log::LogPrefix(const char *Prefix, const char *Format, ...) {
+ char Message[LOG_BUFFERSIZE];
+ char ExtFormat[LOG_BUFFERSIZE];
+
+ // If the issue has ceased at the beginning of a new line, the new issue to begin with the prefix
+ ExtFormat[0] = 0;
+ if (_LineBegin) {
+ snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
+ _LineBegin = false;
+ }
+ // Format String pass line by line and each line with the initial prefix
+ for (;;) {
+ const char *NextLine = strstr(Format, "\n");
+ if (!NextLine || *(NextLine + strlen("\n")) == 0) {
+ snprintf(ExtFormat, sizeof(ExtFormat), "%s%s", ExtFormat, Format);
+ if (NextLine) _LineBegin = true;
+ break;
+ } else {
+ strncat(ExtFormat, Format, (NextLine - Format) + strlen("\n"));
+ snprintf(ExtFormat, sizeof(ExtFormat), "%s%s: ", ExtFormat, Prefix);
+ }
+
+ Format = NextLine + strlen("\n");
+ }
+
+ // Create message
+ va_list ArgList;
+ va_start(ArgList, Format);
+ vsnprintf(Message, sizeof(Message), ExtFormat, ArgList);
+
+ // Log the message
+ _WriteLog(Message);
+
+ _FlushLog();
+}
+
+void BS_Log::LogDecorated(const char *Format, ...) {
+ // Nachricht erzeugen
+ char Message[LOG_BUFFERSIZE];
+ va_list ArgList;
+ va_start(ArgList, Format);
+ vsnprintf(Message, sizeof(Message), Format, ArgList);
+
+ // Zweiten Prefix erzeugen, falls gewünscht
+ char SecondaryPrefix[1024];
+ if (_File && _Line)
+ snprintf(SecondaryPrefix, sizeof(SecondaryPrefix), "(file: %s, line: %d) - ", _File, _Line);
+
+ // Nachricht zeilenweise ausgeben und an jeden Zeilenanfang das Präfix setzen
+ char *MessageWalker = Message;
+ for (;;) {
+ char *NextLine = strstr(MessageWalker, "\n");
+ if (NextLine) {
+ *NextLine = 0;
+ if (_LineBegin) {
+ _WriteLog(_Prefix);
+ if (_File && _Line)
+ _WriteLog(SecondaryPrefix);
+ }
+ _WriteLog(MessageWalker);
+ _WriteLog("\n");
+ MessageWalker = NextLine + sizeof("\n") - 1;
+ _LineBegin = true;
+ } else {
+ if (_LineBegin) {
+ _WriteLog(_Prefix);
+ if (_File && _Line)
+ _WriteLog(SecondaryPrefix);
+ }
+ _WriteLog(MessageWalker);
+ _LineBegin = false;
+ break;
+ }
+ }
+
+ // Falls gewünscht, wird ans Ende der Nachricht automatisch ein Newline angehängt.
+ if (_AutoNewline) {
+ _WriteLog("\n");
+ _LineBegin = true;
+ }
+
+ // Pseudoparameter zurücksetzen
+ _Prefix = NULL;
+ _File = 0;
+ _Line = 0;
+ _AutoNewline = false;
+
+ _FlushLog();
+}
+
+int BS_Log::_WriteLog(const char *Message) {
+ if (!_LogFile) if (!_CreateLog()) return false;
+
+ Common::Array<LOG_LISTENER_CALLBACK>::iterator Iter = _LogListener.begin();
+ for (; Iter != _LogListener.end(); ++Iter)
+ (*Iter)(Message);
+
+ _LogFile->writeString(Message);
+
+ return true;
+}
+
+void BS_Log::_FlushLog() {
+ _LogFile->flush();
+}
+
+void (*BS_LogPtr)(const char *, ...) = BS_Log::Log;
+
+void BS_Log_C(const char *Message) {
+ BS_LogPtr(Message);
+}
+
+#else
+
+void BS_Log_C(const char *Message) {};
+
+#endif
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/log.h b/engines/sword25/kernel/log.h
new file mode 100644
index 0000000000..1fe9ff4ed6
--- /dev/null
+++ b/engines/sword25/kernel/log.h
@@ -0,0 +1,147 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_LOG_H
+#define SWORD25_LOG_H
+
+// Includes
+#include "common/array.h"
+#include "common/file.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// Logging soll nur stattfinden wenn es aktiviert ist
+#ifdef BS_ACTIVATE_LOGGING
+
+// Logging-Makros
+#define BS_LOG BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::LogDecorated
+#define BS_LOGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_WARNING BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::LogDecorated
+#define BS_LOG_WARNINGLN BS_Log::SetPrefix(BS_LOG_PREFIX ": WARNING - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_ERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::LogDecorated
+#define BS_LOG_ERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR - "), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+#define BS_LOG_EXTERROR BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::LogDecorated
+#define BS_LOG_EXTERRORLN BS_Log::SetPrefix(BS_LOG_PREFIX ": ERROR "), BS_Log::SetFile(__FILE__), BS_Log::SetLine(__LINE__), BS_Log::SetAutoNewline(true), BS_Log::LogDecorated
+
+// Die Version der Logging-Klasse mit aktiviertem Logging
+class BS_Log {
+public:
+ static void Clear();
+ static void Log(const char *Format, ...);
+ static void LogPrefix(const char *Prefix, const char *Format, ...);
+ static void LogDecorated(const char *Format, ...);
+
+ static void SetPrefix(const char *Prefix) {
+ _Prefix = Prefix;
+ }
+ static void SetFile(const char *File) {
+ _File = File;
+ }
+ static void SetLine(int Line) {
+ _Line = Line;
+ }
+ static void SetAutoNewline(bool AutoNewline) {
+ _AutoNewline = AutoNewline;
+ }
+
+ typedef void (*LOG_LISTENER_CALLBACK)(const char *);
+ static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {
+ _LogListener.push_back(Callback);
+ }
+ static bool IsListenerRegistered(LOG_LISTENER_CALLBACK Callback) {
+ Common::Array<LOG_LISTENER_CALLBACK>::iterator i;
+ for (i = _LogListener.begin(); i != _LogListener.end(); ++i) {
+ if (**i == Callback)
+ return true;
+ }
+ return false;
+ }
+ static void _CloseLog();
+
+private:
+ static Common::WriteStream *_LogFile;
+ static bool _LineBegin;
+ static const char *_Prefix;
+ static const char *_File;
+ static int _Line;
+ static bool _AutoNewline;
+ static Common::Array<LOG_LISTENER_CALLBACK> _LogListener;
+
+ static bool _CreateLog();
+
+ static int _WriteLog(const char *Message);
+ static void _FlushLog();
+};
+
+// Auxiliary function that allows to log C functions (needed for Lua).
+#define BS_Log_C error
+
+
+#else
+
+// Logging-Macros
+#define BS_LOG
+#define BS_LOGLN
+#define BS_LOG_WARNING
+#define BS_LOG_WARNINGLN
+#define BS_LOG_ERROR
+#define BS_LOG_ERRORLN
+#define BS_LOG_EXTERROR
+#define BS_LOG_EXTERRORLN
+
+// The version of the logging class with logging disabled
+class BS_Log {
+public:
+ // This version implements all the various methods as empty stubs
+ static void Log(const char *Text, ...) {};
+ static void LogPrefix(const char *Prefix, const char *Format, ...) {};
+ static void LogDecorated(const char *Format, ...) {};
+
+ static void SetPrefix(const char *Prefix) {};
+ static void SetFile(const char *File) {};
+ static void SetLine(int Line) {};
+ static void SetAutoNewline(bool AutoNewline) {};
+
+ typedef void (*LOG_LISTENER_CALLBACK)(const char *);
+ static void RegisterLogListener(LOG_LISTENER_CALLBACK Callback) {};
+};
+
+#define BS_Log_C error
+
+#endif
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/objectregistry.h b/engines/sword25/kernel/objectregistry.h
new file mode 100644
index 0000000000..dc702f2d75
--- /dev/null
+++ b/engines/sword25/kernel/objectregistry.h
@@ -0,0 +1,175 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_OBJECTREGISTRY_H
+#define SWORD25_OBJECTREGISTRY_H
+
+#include "common/func.h"
+#include "common/hashmap.h"
+#include "sword25/kernel/bs_stdint.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+template<typename T>
+class ObjectRegistry {
+public:
+ ObjectRegistry() : _nextHandle(1) {}
+ virtual ~ObjectRegistry() {}
+
+ uint registerObject(T *objectPtr) {
+ // Null-Pointer können nicht registriert werden.
+ if (objectPtr == 0) {
+ logErrorLn("Cannot register a null pointer.");
+ return 0;
+ }
+
+ // Falls das Objekt bereits registriert wurde, wird eine Warnung ausgeben und das Handle zurückgeben.
+ uint handle = findHandleByPtr(objectPtr);
+ if (handle != 0) {
+ logWarningLn("Tried to register a object that was already registered.");
+ return handle;
+ }
+ // Ansonsten wird das Objekt in beide Maps eingetragen und das neue Handle zurückgeben.
+ else {
+ _handle2PtrMap[_nextHandle] = objectPtr;
+ _ptr2HandleMap[objectPtr] = _nextHandle;
+
+ return _nextHandle++;
+ }
+ }
+
+ uint registerObject(T *objectPtr, uint handle) {
+ // Null-Pointer und Null-Handle können nicht registriert werden.
+ if (objectPtr == 0 || handle == 0) {
+ logErrorLn("Cannot register a null pointer or a null handle.");
+ return 0;
+ }
+
+ // Falls das Objekt bereits registriert wurde, wird ein Fehler ausgegeben und 0 zurückgeben.
+ uint handleTest = findHandleByPtr(objectPtr);
+ if (handleTest != 0) {
+ logErrorLn("Tried to register a object that was already registered.");
+ return 0;
+ }
+ // Falls das Handle bereits vergeben ist, wird ein Fehler ausgegeben und 0 zurückgegeben.
+ else if (findPtrByHandle(handle) != 0) {
+ logErrorLn("Tried to register a handle that is already taken.");
+ return 0;
+ }
+ // Ansonsten wird das Objekt in beide Maps eingetragen und das gewünschte Handle zurückgeben.
+ else {
+ _handle2PtrMap[handle] = objectPtr;
+ _ptr2HandleMap[objectPtr] = handle;
+
+ // Falls das vergebene Handle größer oder gleich dem nächsten automatische vergebenen Handle ist, wird das nächste automatisch
+ // vergebene Handle erhöht.
+ if (handle >= _nextHandle)
+ _nextHandle = handle + 1;
+
+ return handle;
+ }
+ }
+
+ void deregisterObject(T *objectPtr) {
+ uint handle = findHandleByPtr(objectPtr);
+
+ if (handle != 0) {
+ // Registriertes Objekt aus beiden Maps entfernen.
+ _handle2PtrMap.erase(findHandleByPtr(objectPtr));
+ _ptr2HandleMap.erase(objectPtr);
+ } else {
+ logWarningLn("Tried to remove a object that was not registered.");
+ }
+ }
+
+ T *resolveHandle(uint handle) {
+ // Zum Handle gehöriges Objekt in der Hash-Map finden.
+ T *objectPtr = findPtrByHandle(handle);
+
+ // Pointer zurückgeben. Im Fehlerfall ist dieser 0.
+ return objectPtr;
+ }
+
+ uint resolvePtr(T *objectPtr) {
+ // Zum Pointer gehöriges Handle in der Hash-Map finden.
+ uint handle = findHandleByPtr(objectPtr);
+
+ // Handle zurückgeben. Im Fehlerfall ist dieses 0.
+ return handle;
+ }
+
+protected:
+ // FIXME: I'm not entirely sure my current hash function is legitimate
+ struct ClassPointer_EqualTo {
+ bool operator()(const T *x, const T *y) const {
+ return x == y;
+ }
+ };
+ struct ClassPointer_Hash {
+ uint operator()(const T *x) const {
+ return static_cast<uint>((int64)x % ((int64)1 << sizeof(uint)));
+ }
+ };
+
+ typedef Common::HashMap<uint, T *> HANDLE2PTR_MAP;
+ typedef Common::HashMap<T *, uint, ClassPointer_Hash, ClassPointer_EqualTo> PTR2HANDLE_MAP;
+
+ HANDLE2PTR_MAP _handle2PtrMap;
+ PTR2HANDLE_MAP _ptr2HandleMap;
+ uint _nextHandle;
+
+ T *findPtrByHandle(uint handle) {
+ // Zum Handle gehörigen Pointer finden.
+ typename HANDLE2PTR_MAP::const_iterator it = _handle2PtrMap.find(handle);
+
+ // Pointer zurückgeben, oder, falls keiner gefunden wurde, 0 zurückgeben.
+ return (it != _handle2PtrMap.end()) ? it->_value : 0;
+ }
+
+ uint findHandleByPtr(T *objectPtr) {
+ // Zum Pointer gehöriges Handle finden.
+ typename PTR2HANDLE_MAP::const_iterator it = _ptr2HandleMap.find(objectPtr);
+
+ // Handle zurückgeben, oder, falls keines gefunden wurde, 0 zurückgeben.
+ return (it != _ptr2HandleMap.end()) ? it->_value : 0;
+ }
+
+ virtual void logErrorLn(const char *message) const = 0;
+ virtual void logWarningLn(const char *message) const = 0;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp
new file mode 100644
index 0000000000..438fa7b222
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.cpp
@@ -0,0 +1,131 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "OUTPUTPERSISTENCEBLOCK"
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/outputpersistenceblock.h"
+
+// -----------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------
+
+namespace {
+const uint INITIAL_BUFFER_SIZE = 1024 * 64;
+}
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+OutputPersistenceBlock::OutputPersistenceBlock() {
+ m_Data.reserve(INITIAL_BUFFER_SIZE);
+}
+
+// -----------------------------------------------------------------------------
+// Writing
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(signed int Value) {
+ WriteMarker(SINT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(uint Value) {
+ WriteMarker(UINT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(float Value) {
+ WriteMarker(FLOAT_MARKER);
+ Value = ConvertEndianessFromSystemToStorage(Value);
+ RawWrite(&Value, sizeof(Value));
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(bool Value) {
+ WriteMarker(BOOL_MARKER);
+
+ uint UIntBool = Value ? 1 : 0;
+ UIntBool = ConvertEndianessFromSystemToStorage(UIntBool);
+ RawWrite(&UIntBool, sizeof(UIntBool));
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(const Common::String &String) {
+ WriteMarker(STRING_MARKER);
+
+ write(String.size());
+ RawWrite(String.c_str(), String.size());
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::write(const void *BufferPtr, size_t Size) {
+ WriteMarker(BLOCK_MARKER);
+
+ write(Size);
+ RawWrite(BufferPtr, Size);
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::WriteMarker(byte Marker) {
+ m_Data.push_back(Marker);
+}
+
+// -----------------------------------------------------------------------------
+
+void OutputPersistenceBlock::RawWrite(const void *DataPtr, size_t Size) {
+ if (Size > 0) {
+ uint OldSize = m_Data.size();
+ m_Data.resize(OldSize + Size);
+ memcpy(&m_Data[OldSize], DataPtr, Size);
+ }
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h
new file mode 100644
index 0000000000..154dbc9763
--- /dev/null
+++ b/engines/sword25/kernel/outputpersistenceblock.h
@@ -0,0 +1,78 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_OUTPUTPERSISTENCEBLOCK_H
+#define SWORD25_OUTPUTPERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistenceblock.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class OutputPersistenceBlock : public PersistenceBlock {
+public:
+ OutputPersistenceBlock();
+
+ void write(signed int Value);
+ void write(uint Value);
+ void write(float Value);
+ void write(bool Value);
+ void write(const Common::String &String);
+ void write(const void *BufferPtr, size_t Size);
+
+ const void *GetData() const {
+ return &m_Data[0];
+ }
+ uint GetDataSize() const {
+ return m_Data.size();
+ }
+
+private:
+ void WriteMarker(byte Marker);
+ void RawWrite(const void *DataPtr, size_t Size);
+
+ Common::Array<byte> m_Data;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/persistable.h b/engines/sword25/kernel/persistable.h
new file mode 100644
index 0000000000..fc314688d5
--- /dev/null
+++ b/engines/sword25/kernel/persistable.h
@@ -0,0 +1,53 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PERSISTABLE_H
+#define SWORD25_PERSISTABLE_H
+
+namespace Sword25 {
+
+class OutputPersistenceBlock;
+class InputPersistenceBlock;
+
+class Persistable {
+public:
+ virtual ~Persistable() {};
+
+ virtual bool persist(OutputPersistenceBlock &writer) = 0;
+ virtual bool unpersist(InputPersistenceBlock &reader) = 0;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/persistenceblock.h b/engines/sword25/kernel/persistenceblock.h
new file mode 100644
index 0000000000..1f043aa68a
--- /dev/null
+++ b/engines/sword25/kernel/persistenceblock.h
@@ -0,0 +1,133 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PERSISTENCEBLOCK_H
+#define SWORD25_PERSISTENCEBLOCK_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class definition
+// -----------------------------------------------------------------------------
+
+class PersistenceBlock {
+public:
+ static uint GetSInt32Size() {
+ return sizeof(signed int) + sizeof(byte);
+ }
+ static uint GetUInt32Size() {
+ return sizeof(uint) + sizeof(byte);
+ }
+ static uint GetFloat32Size() {
+ return sizeof(float) + sizeof(byte);
+ }
+ static uint GetBoolSize() {
+ return sizeof(byte) + sizeof(byte);
+ }
+ static uint GetStringSize(const Common::String &String) {
+ return static_cast<uint>(sizeof(uint) + String.size() + sizeof(byte));
+ }
+
+protected:
+ enum {
+ SINT_MARKER,
+ UINT_MARKER,
+ FLOAT_MARKER,
+ STRING_MARKER,
+ BOOL_MARKER,
+ BLOCK_MARKER
+ };
+
+ // -----------------------------------------------------------------------------
+ // Endianess Conversions
+ // -----------------------------------------------------------------------------
+ //
+ // Everything is stored in Little Endian
+ // Big Endian Systems will need to be byte swapped during both saving and reading of saved values
+ //
+
+ template<typename T>
+ static T ConvertEndianessFromSystemToStorage(T Value) {
+ if (IsBigEndian()) ReverseByteOrder(&Value);
+ return Value;
+ }
+
+ template<typename T>
+ static T ConvertEndianessFromStorageToSystem(T Value) {
+ if (IsBigEndian()) ReverseByteOrder(&Value);
+ return Value;
+ }
+
+private:
+ static bool IsBigEndian() {
+ uint Dummy = 1;
+ byte *DummyPtr = reinterpret_cast<byte *>(&Dummy);
+ return DummyPtr[0] == 0;
+ }
+
+ template<typename T>
+ static void Swap(T &One, T &Two) {
+ T Temp = One;
+ One = Two;
+ Two = Temp;
+ }
+
+ static void ReverseByteOrder(void *Ptr) {
+ // Reverses the byte order of the 32-bit word pointed to by Ptr
+ byte *CharPtr = static_cast<byte *>(Ptr);
+ Swap(CharPtr[0], CharPtr[3]);
+ Swap(CharPtr[1], CharPtr[2]);
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Compile time asserts
+// -----------------------------------------------------------------------------
+
+#define CTASSERT(ex) typedef char ctassert_type[(ex) ? 1 : -1]
+CTASSERT(sizeof(byte) == 1);
+CTASSERT(sizeof(signed int) == 4);
+CTASSERT(sizeof(uint) == 4);
+CTASSERT(sizeof(float) == 4);
+#undef CTASSERT
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/persistenceservice.cpp b/engines/sword25/kernel/persistenceservice.cpp
new file mode 100644
index 0000000000..871bc37e2a
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.cpp
@@ -0,0 +1,465 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/fs.h"
+#include "common/savefile.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/persistenceservice.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/filesystemutil.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/sfx/soundengine.h"
+#include "sword25/input/inputengine.h"
+#include "sword25/math/regionregistry.h"
+#include "sword25/script/script.h"
+#include <zlib.h>
+
+#define BS_LOG_PREFIX "PERSISTENCESERVICE"
+
+// -----------------------------------------------------------------------------
+// Constants and utility functions
+// -----------------------------------------------------------------------------
+
+namespace Sword25 {
+const char *SAVEGAME_EXTENSION = ".b25s";
+const char *SAVEGAME_DIRECTORY = "saves";
+const char *FILE_MARKER = "BS25SAVEGAME";
+const uint SLOT_COUNT = 18;
+const uint FILE_COPY_BUFFER_SIZE = 1024 * 10;
+const char *VERSIONID = "SCUMMVM1";
+
+// -------------------------------------------------------------------------
+
+Common::String GenerateSavegameFilename(uint slotID) {
+ char buffer[10];
+ sprintf(buffer, "%d%s", slotID, SAVEGAME_EXTENSION);
+ return Common::String(buffer);
+}
+
+// -------------------------------------------------------------------------
+
+Common::String GenerateSavegamePath(uint SlotID) {
+ Common::FSNode folder(PersistenceService::GetSavegameDirectory());
+
+ return folder.getChild(GenerateSavegameFilename(SlotID)).getPath();
+}
+
+// -------------------------------------------------------------------------
+
+Common::String FormatTimestamp(TimeDate Time) {
+ // In the original BS2.5 engine, this used a local object to show the date/time as as a string.
+ // For now in ScummVM it's being hardcoded to 'dd-MON-yyyy hh:mm:ss'
+ Common::String monthList[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ char buffer[100];
+ snprintf(buffer, 100, "%.2d-%s-%.4d %.2d:%.2d:%.2d",
+ Time.tm_mday, monthList[Time.tm_mon].c_str(), 1900 + Time.tm_year,
+ Time.tm_hour, Time.tm_min, Time.tm_sec
+ );
+
+ return Common::String(buffer);
+}
+
+// -------------------------------------------------------------------------
+
+Common::String LoadString(Common::InSaveFile *In, uint MaxSize = 999) {
+ Common::String Result;
+
+ char ch = (char)In->readByte();
+ while ((ch != '\0') && (ch != ' ')) {
+ Result += ch;
+ if (Result.size() >= MaxSize) break;
+ ch = (char)In->readByte();
+ }
+
+ return Result;
+}
+
+}
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Private Implementation
+// -----------------------------------------------------------------------------
+
+struct SavegameInformation {
+ bool IsOccupied;
+ bool IsCompatible;
+ Common::String Description;
+ Common::String Filename;
+ uint GamedataLength;
+ uint GamedataOffset;
+ uint GamedataUncompressedLength;
+
+ SavegameInformation() {
+ Clear();
+ }
+
+ void Clear() {
+ IsOccupied = false;
+ IsCompatible = false;
+ Description = "";
+ Filename = "";
+ GamedataLength = 0;
+ GamedataOffset = 0;
+ GamedataUncompressedLength = 0;
+ }
+};
+
+struct PersistenceService::Impl {
+ SavegameInformation m_SavegameInformations[SLOT_COUNT];
+
+ // -----------------------------------------------------------------------------
+
+ Impl() {
+ ReloadSlots();
+ }
+
+ // -----------------------------------------------------------------------------
+
+ void ReloadSlots() {
+ // Über alle Spielstanddateien iterieren und deren Infos einlesen.
+ for (uint i = 0; i < SLOT_COUNT; ++i) {
+ ReadSlotSavegameInformation(i);
+ }
+ }
+
+ void ReadSlotSavegameInformation(uint SlotID) {
+ // Aktuelle Slotinformationen in den Ausgangszustand versetzen, er wird im Folgenden neu gefüllt.
+ SavegameInformation &CurSavegameInfo = m_SavegameInformations[SlotID];
+ CurSavegameInfo.Clear();
+
+ // Den Dateinamen für den Spielstand des Slots generieren.
+ Common::String Filename = GenerateSavegameFilename(SlotID);
+
+ // Try to open the savegame for loading
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ Common::InSaveFile *File = sfm->openForLoading(Filename);
+
+ if (File) {
+ // Read in the header
+ Common::String StoredMarker = LoadString(File);
+ Common::String StoredVersionID = LoadString(File);
+ Common::String gameDataLength = LoadString(File);
+ CurSavegameInfo.GamedataLength = atoi(gameDataLength.c_str());
+ Common::String gamedataUncompressedLength = LoadString(File);
+ CurSavegameInfo.GamedataUncompressedLength = atoi(gamedataUncompressedLength.c_str());
+
+ // If the header can be read in and is detected to be valid, we will have a valid file
+ if (StoredMarker == FILE_MARKER) {
+ // Der Slot wird als belegt markiert.
+ CurSavegameInfo.IsOccupied = true;
+ // Speichern, ob der Spielstand kompatibel mit der aktuellen Engine-Version ist.
+ CurSavegameInfo.IsCompatible = (StoredVersionID == Common::String(VERSIONID));
+ // Dateinamen des Spielstandes speichern.
+ CurSavegameInfo.Filename = GenerateSavegameFilename(SlotID);
+ // Die Beschreibung des Spielstandes besteht aus einer textuellen Darstellung des Änderungsdatums der Spielstanddatei.
+ CurSavegameInfo.Description = FormatTimestamp(FileSystemUtil::GetInstance().GetFileTime(Filename));
+ // Den Offset zu den gespeicherten Spieldaten innerhalb der Datei speichern.
+ // Dieses entspricht der aktuellen Position, da nach der letzten Headerinformation noch ein Leerzeichen als trenner folgt.
+ CurSavegameInfo.GamedataOffset = static_cast<uint>(File->pos());
+ }
+
+ delete File;
+ }
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Construction / Destruction
+// -----------------------------------------------------------------------------
+
+PersistenceService &PersistenceService::GetInstance() {
+ static PersistenceService Instance;
+ return Instance;
+}
+
+// -----------------------------------------------------------------------------
+
+PersistenceService::PersistenceService() : m_impl(new Impl) {
+}
+
+// -----------------------------------------------------------------------------
+
+PersistenceService::~PersistenceService() {
+ delete m_impl;
+}
+
+// -----------------------------------------------------------------------------
+// Implementation
+// -----------------------------------------------------------------------------
+
+void PersistenceService::ReloadSlots() {
+ m_impl->ReloadSlots();
+}
+
+// -----------------------------------------------------------------------------
+
+uint PersistenceService::GetSlotCount() {
+ return SLOT_COUNT;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::String PersistenceService::GetSavegameDirectory() {
+ Common::FSNode node(FileSystemUtil::GetInstance().GetUserdataDirectory());
+ Common::FSNode childNode = node.getChild(SAVEGAME_DIRECTORY);
+
+ // Try and return the path using the savegame subfolder. But if doesn't exist, fall back on the data directory
+ if (childNode.exists())
+ return childNode.getPath();
+
+ return node.getPath();
+}
+
+// -----------------------------------------------------------------------------
+
+namespace {
+bool CheckSlotID(uint SlotID) {
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to access an invalid slot (%d). Only slot ids from 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ } else {
+ return true;
+ }
+}
+}
+
+// -----------------------------------------------------------------------------
+
+bool PersistenceService::IsSlotOccupied(uint SlotID) {
+ if (!CheckSlotID(SlotID)) return false;
+ return m_impl->m_SavegameInformations[SlotID].IsOccupied;
+}
+
+// -----------------------------------------------------------------------------
+
+bool PersistenceService::IsSavegameCompatible(uint SlotID) {
+ if (!CheckSlotID(SlotID)) return false;
+ return m_impl->m_SavegameInformations[SlotID].IsCompatible;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::String &PersistenceService::GetSavegameDescription(uint SlotID) {
+ static Common::String EmptyString;
+ if (!CheckSlotID(SlotID)) return EmptyString;
+ return m_impl->m_SavegameInformations[SlotID].Description;
+}
+
+// -----------------------------------------------------------------------------
+
+Common::String &PersistenceService::GetSavegameFilename(uint SlotID) {
+ static Common::String EmptyString;
+ if (!CheckSlotID(SlotID)) return EmptyString;
+ return m_impl->m_SavegameInformations[SlotID].Filename;
+}
+
+// -----------------------------------------------------------------------------
+
+bool PersistenceService::SaveGame(uint SlotID, const Common::String &ScreenshotFilename) {
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to save to an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ }
+
+ // Dateinamen erzeugen.
+ Common::String Filename = GenerateSavegameFilename(SlotID);
+
+ // Sicherstellen, dass das Verzeichnis für die Spielstanddateien existiert.
+ FileSystemUtil::GetInstance().CreateDirectory(GetSavegameDirectory());
+
+ // Spielstanddatei öffnen und die Headerdaten schreiben.
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ Common::OutSaveFile *File = sfm->openForSaving(Filename);
+
+ File->writeString(FILE_MARKER);
+ File->writeByte(' ');
+ File->writeString(VERSIONID);
+ File->writeByte(' ');
+
+ if (File->err()) {
+ error("Unable to write header data to savegame file \"%s\".", Filename.c_str());
+ }
+
+ // Alle notwendigen Module persistieren.
+ OutputPersistenceBlock Writer;
+ bool Success = true;
+ Success &= Kernel::GetInstance()->GetScript()->persist(Writer);
+ Success &= RegionRegistry::getInstance().persist(Writer);
+ Success &= Kernel::GetInstance()->GetGfx()->persist(Writer);
+ Success &= Kernel::GetInstance()->GetSfx()->persist(Writer);
+ Success &= Kernel::GetInstance()->GetInput()->persist(Writer);
+ if (!Success) {
+ error("Unable to persist modules for savegame file \"%s\".", Filename.c_str());
+ }
+
+ // Daten komprimieren.
+ uLongf CompressedLength = Writer.GetDataSize() + (Writer.GetDataSize() + 500) / 1000 + 12;
+ Bytef *CompressionBuffer = new Bytef[CompressedLength];
+
+ if (compress2(&CompressionBuffer[0], &CompressedLength, reinterpret_cast<const Bytef *>(Writer.GetData()), Writer.GetDataSize(), 6) != Z_OK) {
+ error("Unable to compress savegame data in savegame file \"%s\".", Filename.c_str());
+ }
+
+ // Länge der komprimierten Daten und der unkomprimierten Daten in die Datei schreiben.
+ char sBuffer[10];
+ snprintf(sBuffer, 10, "%ld", CompressedLength);
+ File->writeString(sBuffer);
+ File->writeByte(' ');
+ snprintf(sBuffer, 10, "%u", Writer.GetDataSize());
+ File->writeString(sBuffer);
+ File->writeByte(' ');
+
+ // Komprimierte Daten in die Datei schreiben.
+ File->write(reinterpret_cast<char *>(&CompressionBuffer[0]), CompressedLength);
+ if (File->err()) {
+ error("Unable to write game data to savegame file \"%s\".", Filename.c_str());
+ }
+
+ // Get the screenshot
+ Common::MemoryReadStream *thumbnail = (static_cast<GraphicEngine *>(
+ Kernel::GetInstance()->GetService("gfx")))->getThumbnail();
+
+ if (thumbnail) {
+ byte *Buffer = new Byte[FILE_COPY_BUFFER_SIZE];
+ while (!thumbnail->eos()) {
+ int bytesRead = thumbnail->read(&Buffer[0], FILE_COPY_BUFFER_SIZE);
+ File->write(&Buffer[0], bytesRead);
+ }
+
+ delete[] Buffer;
+ } else {
+ BS_LOG_WARNINGLN("The screenshot file \"%s\" does not exist. Savegame is written without a screenshot.", Filename.c_str());
+ }
+
+ // Savegameinformationen für diesen Slot aktualisieren.
+ m_impl->ReadSlotSavegameInformation(SlotID);
+
+ File->finalize();
+ delete File;
+ delete[] CompressionBuffer;
+
+ // Erfolg signalisieren.
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+bool PersistenceService::LoadGame(uint SlotID) {
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ Common::InSaveFile *File;
+
+ // Überprüfen, ob die Slot-ID zulässig ist.
+ if (SlotID >= SLOT_COUNT) {
+ BS_LOG_ERRORLN("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", SlotID, SLOT_COUNT - 1);
+ return false;
+ }
+
+ SavegameInformation &CurSavegameInfo = m_impl->m_SavegameInformations[SlotID];
+
+ // Überprüfen, ob der Slot belegt ist.
+ if (!CurSavegameInfo.IsOccupied) {
+ BS_LOG_ERRORLN("Tried to load from an empty slot (%d).", SlotID);
+ return false;
+ }
+
+ // Überprüfen, ob der Spielstand im angegebenen Slot mit der aktuellen Engine-Version kompatibel ist.
+ // Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen,
+ // da sich die Versions-ID bei jeder Codeänderung mitändert.
+#ifndef DEBUG
+ if (!CurSavegameInfo.IsCompatible) {
+ BS_LOG_ERRORLN("Tried to load a savegame (%d) that is not compatible with this engine version.", SlotID);
+ return false;
+ }
+#endif
+
+ byte *CompressedDataBuffer = new byte[CurSavegameInfo.GamedataLength];
+ byte *UncompressedDataBuffer = new Bytef[CurSavegameInfo.GamedataUncompressedLength];
+
+ File = sfm->openForLoading(GenerateSavegameFilename(SlotID));
+
+ File->seek(CurSavegameInfo.GamedataOffset);
+ File->read(reinterpret_cast<char *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength);
+ if (File->err()) {
+ BS_LOG_ERRORLN("Unable to load the gamedata from the savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ delete[] CompressedDataBuffer;
+ delete[] UncompressedDataBuffer;
+ return false;
+ }
+
+ // Spieldaten dekomprimieren.
+ uLongf UncompressedBufferSize = CurSavegameInfo.GamedataUncompressedLength;
+ if (uncompress(reinterpret_cast<Bytef *>(&UncompressedDataBuffer[0]), &UncompressedBufferSize,
+ reinterpret_cast<Bytef *>(&CompressedDataBuffer[0]), CurSavegameInfo.GamedataLength) != Z_OK) {
+ BS_LOG_ERRORLN("Unable to decompress the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ delete[] UncompressedDataBuffer;
+ delete[] CompressedDataBuffer;
+ delete File;
+ return false;
+ }
+
+ InputPersistenceBlock Reader(&UncompressedDataBuffer[0], CurSavegameInfo.GamedataUncompressedLength);
+
+ // Einzelne Engine-Module depersistieren.
+ bool Success = true;
+ Success &= Kernel::GetInstance()->GetScript()->unpersist(Reader);
+ // Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden.
+ Success &= RegionRegistry::getInstance().unpersist(Reader);
+ Success &= Kernel::GetInstance()->GetGfx()->unpersist(Reader);
+ Success &= Kernel::GetInstance()->GetSfx()->unpersist(Reader);
+ Success &= Kernel::GetInstance()->GetInput()->unpersist(Reader);
+
+ delete[] CompressedDataBuffer;
+ delete[] UncompressedDataBuffer;
+ delete File;
+
+ if (!Success) {
+ BS_LOG_ERRORLN("Unable to unpersist the gamedata from savegame file \"%s\".", CurSavegameInfo.Filename.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/persistenceservice.h b/engines/sword25/kernel/persistenceservice.h
new file mode 100644
index 0000000000..d14185eac2
--- /dev/null
+++ b/engines/sword25/kernel/persistenceservice.h
@@ -0,0 +1,84 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_PERSISTENCESERVICE_H
+#define SWORD25_PERSISTENCESERVICE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// -----------------------------------------------------------------------------
+// Class declaration
+// -----------------------------------------------------------------------------
+
+class PersistenceService {
+public:
+ PersistenceService();
+ virtual ~PersistenceService();
+
+ // -----------------------------------------------------------------------------
+ // Singleton Method
+ // -----------------------------------------------------------------------------
+
+ static PersistenceService &GetInstance();
+
+ // -----------------------------------------------------------------------------
+ // Interface
+ // -----------------------------------------------------------------------------
+
+ static uint GetSlotCount();
+ static Common::String GetSavegameDirectory();
+
+ void ReloadSlots();
+ bool IsSlotOccupied(uint SlotID);
+ bool IsSavegameCompatible(uint SlotID);
+ Common::String &GetSavegameDescription(uint SlotID);
+ Common::String &GetSavegameFilename(uint SlotID);
+
+ bool SaveGame(uint SlotID, const Common::String &ScreenshotFilename);
+ bool LoadGame(uint SlotID);
+
+private:
+ struct Impl;
+ Impl *m_impl;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/resmanager.cpp b/engines/sword25/kernel/resmanager.cpp
new file mode 100644
index 0000000000..9e80f32f8d
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.cpp
@@ -0,0 +1,336 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/resmanager.h"
+
+#include "sword25/kernel/resource.h"
+#include "sword25/kernel/resservice.h"
+#include "sword25/kernel/string.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RESOURCEMANAGER"
+
+ResourceManager::~ResourceManager() {
+ // Clear all unlocked resources
+ EmptyCache();
+
+ // All remaining resources are not released, so print warnings and release
+ Common::List<Resource *>::iterator Iter = m_Resources.begin();
+ for (; Iter != m_Resources.end(); ++Iter) {
+ BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*Iter)->getFileName().c_str());
+
+ // Set the lock count to zero
+ while ((*Iter)->GetLockCount() > 0) {
+ (*Iter)->release();
+ };
+
+ // Delete the resource
+ delete(*Iter);
+ }
+}
+
+/**
+ * Returns a resource by it's ordinal index. Returns NULL if any error occurs
+ * Note: This method is not optimised for speed and should be used only for debugging purposes
+ * @param Ord Ordinal number of the resource. Must be between 0 and GetResourceCount() - 1.
+ */
+Resource *ResourceManager::GetResourceByOrdinal(int Ord) const {
+ // Überprüfen ob der Index Ord innerhald der Listengrenzen liegt.
+ if (Ord < 0 || Ord >= GetResourceCount()) {
+ BS_LOG_ERRORLN("Resource ordinal (%d) out of bounds (0 - %d).", Ord, GetResourceCount() - 1);
+ return NULL;
+ }
+
+ // Liste durchlaufen und die Resource mit dem gewünschten Index zurückgeben.
+ int CurOrd = 0;
+ Common::List<Resource *>::const_iterator Iter = m_Resources.begin();
+ for (; Iter != m_Resources.end(); ++Iter, ++CurOrd) {
+ if (CurOrd == Ord)
+ return (*Iter);
+ }
+
+ // Die Ausführung sollte nie an diesem Punkt ankommen.
+ BS_LOG_EXTERRORLN("Execution reached unexpected point.");
+ return NULL;
+}
+
+/**
+ * Registers a RegisterResourceService. This method is the constructor of
+ * BS_ResourceService, and thus helps all resource services in the ResourceManager list
+ * @param pService Which service
+ */
+bool ResourceManager::RegisterResourceService(ResourceService *pService) {
+ if (!pService) {
+ BS_LOG_ERRORLN("Can't register NULL resource service.");
+ return false;
+ }
+
+ m_ResourceServices.push_back(pService);
+
+ return true;
+}
+
+/**
+ * Deletes resources as necessary until the specified memory limit is not being exceeded.
+ */
+void ResourceManager::DeleteResourcesIfNecessary() {
+ // If enough memory is available, or no resources are loaded, then the function can immediately end
+ if (m_KernelPtr->GetUsedMemory() < m_MaxMemoryUsage || m_Resources.empty()) return;
+
+ // Keep deleting resources until the memory usage of the process falls below the set maximum limit.
+ // The list is processed backwards in order to first release those resources who have been
+ // not been accessed for the longest
+ Common::List<Resource *>::iterator Iter = m_Resources.end();
+ do {
+ --Iter;
+
+ // The resource may be released only if it isn't locked
+ if ((*Iter)->GetLockCount() == 0) Iter = DeleteResource(*Iter);
+ } while (Iter != m_Resources.begin() && m_KernelPtr->GetUsedMemory() > m_MaxMemoryUsage);
+}
+
+/**
+ * Releases all resources that are not locked.
+ **/
+void ResourceManager::EmptyCache() {
+ // Scan through the resource list
+ Common::List<Resource *>::iterator Iter = m_Resources.begin();
+ while (Iter != m_Resources.end()) {
+ if ((*Iter)->GetLockCount() == 0) {
+ // Delete the resource
+ Iter = DeleteResource(*Iter);
+ } else
+ ++Iter;
+ }
+}
+
+/**
+ * Returns a requested resource. If any error occurs, returns NULL
+ * @param FileName Filename of resource
+ */
+Resource *ResourceManager::RequestResource(const Common::String &FileName) {
+ // Get the absolute path to the file
+ Common::String UniqueFileName = GetUniqueFileName(FileName);
+ if (UniqueFileName == "")
+ return NULL;
+
+ // Determine whether the resource is already loaded
+ // If the resource is found, it will be placed at the head of the resource list and returned
+ {
+ Resource *pResource = GetResource(UniqueFileName);
+ if (pResource) {
+ MoveToFront(pResource);
+ (pResource)->AddReference();
+ return pResource;
+ }
+ }
+
+ // The resource was not found, therefore, must not be loaded yet
+ if (m_LogCacheMiss) BS_LOG_WARNINGLN("\"%s\" was not precached.", UniqueFileName.c_str());
+
+ Resource *pResource;
+ if ((pResource = loadResource(UniqueFileName))) {
+ pResource->AddReference();
+ return pResource;
+ }
+
+ return NULL;
+}
+
+/**
+ * Loads a resource into the cache
+ * @param FileName The filename of the resource to be cached
+ * @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
+ * This is useful for files that may have changed in the interim
+ */
+bool ResourceManager::PrecacheResource(const Common::String &FileName, bool ForceReload) {
+ // Get the absolute path to the file
+ Common::String UniqueFileName = GetUniqueFileName(FileName);
+ if (UniqueFileName == "")
+ return false;
+
+ Resource *ResourcePtr = GetResource(UniqueFileName);
+
+ if (ForceReload && ResourcePtr) {
+ if (ResourcePtr->GetLockCount()) {
+ BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", FileName.c_str());
+ return false;
+ } else {
+ DeleteResource(ResourcePtr);
+ ResourcePtr = 0;
+ }
+ }
+
+ if (!ResourcePtr && loadResource(UniqueFileName) == NULL) {
+ BS_LOG_ERRORLN("Could not precache \"%s\",", FileName.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Moves a resource to the top of the resource list
+ * @param pResource The resource
+ */
+void ResourceManager::MoveToFront(Resource *pResource) {
+ // Erase the resource from it's current position
+ m_Resources.erase(pResource->_iterator);
+ // Re-add the resource at the front of the list
+ m_Resources.push_front(pResource);
+ // Reset the resource iterator to the repositioned item
+ pResource->_iterator = m_Resources.begin();
+}
+
+/**
+ * Loads a resource and updates the m_UsedMemory total
+ *
+ * The resource must not already be loaded
+ * @param FileName The unique filename of the resource to be loaded
+ */
+Resource *ResourceManager::loadResource(const Common::String &fileName) {
+ // ResourceService finden, der die Resource laden kann.
+ for (uint i = 0; i < m_ResourceServices.size(); ++i) {
+ if (m_ResourceServices[i]->canLoadResource(fileName)) {
+ // If more memory is desired, memory must be released
+ DeleteResourcesIfNecessary();
+
+ // Load the resource
+ Resource *pResource;
+ if (!(pResource = m_ResourceServices[i]->loadResource(fileName))) {
+ BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", fileName.c_str());
+ return NULL;
+ }
+
+ // Add the resource to the front of the list
+ m_Resources.push_front(pResource);
+ pResource->_iterator = m_Resources.begin();
+
+ // Also store the resource in the hash table for quick lookup
+ m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].push_front(pResource);
+
+ return pResource;
+ }
+ }
+
+ BS_LOG_ERRORLN("Could not find a service that can load \"%s\".", fileName.c_str());
+ return NULL;
+}
+
+/**
+ * Returns the full path of a given resource filename.
+ * It will return an empty string if a path could not be created.
+*/
+Common::String ResourceManager::GetUniqueFileName(const Common::String &FileName) const {
+ // Get a pointer to the package manager
+ PackageManager *pPackage = (PackageManager *)m_KernelPtr->GetService("package");
+ if (!pPackage) {
+ BS_LOG_ERRORLN("Could not get package manager.");
+ return Common::String("");
+ }
+
+ // Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen
+ Common::String UniqueFileName = pPackage->getAbsolutePath(FileName);
+ if (UniqueFileName == "")
+ BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", FileName.c_str());
+
+ return UniqueFileName;
+}
+
+/**
+ * Deletes a resource, removes it from the lists, and updates m_UsedMemory
+ */
+Common::List<Resource *>::iterator ResourceManager::DeleteResource(Resource *pResource) {
+ // Remove the resource from the hash table
+ m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].remove(pResource);
+
+ Resource *pDummy = pResource;
+
+ // Delete the resource from the resource list
+ Common::List<Resource *>::iterator Result = m_Resources.erase(pResource->_iterator);
+
+ // Delete the resource
+ delete(pDummy);
+
+ // Return the iterator
+ return Result;
+}
+
+/**
+ * Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
+ * @param UniqueFileName The absolute path and filename
+ * Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist.
+ */
+Resource *ResourceManager::GetResource(const Common::String &UniqueFileName) const {
+ // Determine whether the resource is already loaded
+ const Common::List<Resource *>& HashBucket = m_ResourceHashTable[
+ BS_String::GetHash(UniqueFileName) % HASH_TABLE_BUCKETS];
+ {
+ Common::List<Resource *>::const_iterator Iter = HashBucket.begin();
+ for (; Iter != HashBucket.end(); ++Iter) {
+ // Wenn die Resource gefunden wurde wird sie zurückgegeben.
+ if ((*Iter)->getFileName() == UniqueFileName)
+ return *Iter;
+ }
+ }
+
+ // Resource wurde nicht gefunden, ist also nicht geladen
+ return NULL;
+}
+
+/**
+ * Writes the names of all currently locked resources to the log file
+ */
+void ResourceManager::DumpLockedResources() {
+ for (Common::List<Resource *>::iterator Iter = m_Resources.begin(); Iter != m_Resources.end(); ++Iter) {
+ if ((*Iter)->GetLockCount() > 0) {
+ BS_LOGLN("%s", (*Iter)->getFileName().c_str());
+ }
+ }
+}
+
+/**
+ * Specifies the maximum amount of memory the engine is allowed to use.
+ * If this value is exceeded, resources will be unloaded to make room. This value is meant
+ * as a guideline, and not as a fixed boundary. It is not guaranteed not to be exceeded;
+ * the whole game engine may still use more memory than any amount specified.
+ */
+void ResourceManager::SetMaxMemoryUsage(uint MaxMemoryUsage) {
+ m_MaxMemoryUsage = MaxMemoryUsage;
+ DeleteResourcesIfNecessary();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/resmanager.h b/engines/sword25/kernel/resmanager.h
new file mode 100644
index 0000000000..578f121fec
--- /dev/null
+++ b/engines/sword25/kernel/resmanager.h
@@ -0,0 +1,193 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RESOURCEMANAGER_H
+#define SWORD25_RESOURCEMANAGER_H
+
+// Includes
+#include "common/list.h"
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// Class definitions
+class ResourceService;
+class Resource;
+class Kernel;
+
+class ResourceManager {
+ friend class Kernel;
+
+public:
+ /**
+ * Returns a requested resource. If any error occurs, returns NULL
+ * @param FileName Filename of resource
+ */
+ Resource *RequestResource(const Common::String &FileName);
+
+ /**
+ * Loads a resource into the cache
+ * @param FileName The filename of the resource to be cached
+ * @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
+ * This is useful for files that may have changed in the interim
+ */
+ bool PrecacheResource(const Common::String &FileName, bool ForceReload = false);
+
+ /**
+ * Returns the number of loaded resources
+ */
+ int GetResourceCount() const {
+ return static_cast<int>(m_Resources.size());
+ }
+
+ /**
+ * Returns a resource by it's ordinal index. Returns NULL if any error occurs
+ * Note: This method is not optimised for speed and should be used only for debugging purposes
+ * @param Ord Ordinal number of the resource. Must be between 0 and GetResourceCount() - 1.
+ */
+ Resource *GetResourceByOrdinal(int Ord) const;
+
+ /**
+ * Registers a RegisterResourceService. This method is the constructor of
+ * BS_ResourceService, and thus helps all resource services in the ResourceManager list
+ * @param pService Which service
+ */
+ bool RegisterResourceService(ResourceService *pService);
+
+ /**
+ * Releases all resources that are not locked.
+ **/
+ void EmptyCache();
+
+ /**
+ * Returns the maximum memory the kernel has used
+ */
+ int GetMaxMemoryUsage() const {
+ return m_MaxMemoryUsage;
+ }
+
+ /**
+ * Specifies the maximum amount of memory the engine is allowed to use.
+ * If this value is exceeded, resources will be unloaded to make room. This value is meant
+ * as a guideline, and not as a fixed boundary. It is not guaranteed not to be exceeded;
+ * the whole game engine may still use more memory than any amount specified.
+ */
+ void SetMaxMemoryUsage(uint MaxMemoryUsage);
+
+ /**
+ * Specifies whether a warning is written to the log when a cache miss occurs.
+ * THe default value is "false".
+ */
+ bool IsLogCacheMiss() const {
+ return m_LogCacheMiss;
+ }
+
+ /**
+ * Sets whether warnings are written to the log if a cache miss occurs.
+ * @param Flag If "true", then future warnings will be logged
+ */
+ void SetLogCacheMiss(bool Flag) {
+ m_LogCacheMiss = Flag;
+ }
+
+ /**
+ * Writes the names of all currently locked resources to the log file
+ */
+ void DumpLockedResources();
+
+private:
+ /**
+ * Creates a new resource manager
+ * Only the BS_Kernel class can generate copies this class. Thus, the constructor is private
+ */
+ ResourceManager(Kernel *pKernel) :
+ m_KernelPtr(pKernel),
+ m_MaxMemoryUsage(100000000),
+ m_LogCacheMiss(false)
+ {};
+ virtual ~ResourceManager();
+
+ enum {
+ HASH_TABLE_BUCKETS = 256
+ };
+
+ /**
+ * Moves a resource to the top of the resource list
+ * @param pResource The resource
+ */
+ void MoveToFront(Resource *pResource);
+
+ /**
+ * Loads a resource and updates the m_UsedMemory total
+ *
+ * The resource must not already be loaded
+ * @param FileName The unique filename of the resource to be loaded
+ */
+ Resource *loadResource(const Common::String &fileName);
+
+ /**
+ * Returns the full path of a given resource filename.
+ * It will return an empty string if a path could not be created.
+ */
+ Common::String GetUniqueFileName(const Common::String &FileName) const;
+
+ /**
+ * Deletes a resource, removes it from the lists, and updates m_UsedMemory
+ */
+ Common::List<Resource *>::iterator DeleteResource(Resource *pResource);
+
+ /**
+ * Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
+ * @param UniqueFileName The absolute path and filename
+ * Gibt einen Pointer auf die angeforderte Resource zurück, oder NULL, wenn die Resourcen nicht geladen ist.
+ */
+ Resource *GetResource(const Common::String &UniqueFileName) const;
+
+ /**
+ * Deletes resources as necessary until the specified memory limit is not being exceeded.
+ */
+ void DeleteResourcesIfNecessary();
+
+ Kernel *m_KernelPtr;
+ uint m_MaxMemoryUsage;
+ Common::Array<ResourceService *> m_ResourceServices;
+ Common::List<Resource *> m_Resources;
+ Common::List<Resource *> m_ResourceHashTable[HASH_TABLE_BUCKETS];
+ bool m_LogCacheMiss;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/resource.cpp b/engines/sword25/kernel/resource.cpp
new file mode 100644
index 0000000000..f6f4f13f68
--- /dev/null
+++ b/engines/sword25/kernel/resource.cpp
@@ -0,0 +1,60 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/resource.h"
+#include "sword25/kernel/string.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "RESOURCE"
+
+Resource::Resource(const Common::String &fileName, RESOURCE_TYPES type) :
+ _type(type),
+ _refCount(0) {
+ BS_ASSERT(Kernel::GetInstance()->GetService("package"));
+
+ _fileName = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"))->getAbsolutePath(fileName);
+ _fileNameHash = BS_String::GetHash(fileName);
+}
+
+void Resource::release() {
+ if (_refCount) {
+ --_refCount;
+ } else
+ BS_LOG_WARNINGLN("Released unlocked resource \"%s\".", _fileName.c_str());
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/resource.h b/engines/sword25/kernel/resource.h
new file mode 100644
index 0000000000..2a4d197138
--- /dev/null
+++ b/engines/sword25/kernel/resource.h
@@ -0,0 +1,118 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RESOURCE_H
+#define SWORD25_RESOURCE_H
+
+#include "common/list.h"
+#include "common/str.h"
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+class Kernel;
+class ResourceManager;
+
+class Resource {
+ friend class ResourceManager;
+
+public:
+ enum RESOURCE_TYPES {
+ TYPE_UNKNOWN,
+ TYPE_BITMAP,
+ TYPE_ANIMATION,
+ TYPE_SOUND,
+ TYPE_FONT
+ };
+
+ Resource(const Common::String &uniqueFileName, RESOURCE_TYPES type);
+
+ /**
+ * Prevents the resource from being released.
+ * @remarks This method allows a resource to be locked multiple times.
+ **/
+ void AddReference() {
+ ++_refCount;
+ }
+
+ /**
+ * Cancels a previous lock
+ * @remarks The resource can still be released more times than it was 'locked', although it is
+ * not recommended.
+ **/
+ void release();
+
+ /**
+ * Returns the current lock count for the resource
+ * @return The current lock count
+ **/
+ int GetLockCount() const {
+ return _refCount;
+ }
+
+ /**
+ * Returns the absolute path of the given resource
+ */
+ const Common::String &getFileName() const {
+ return _fileName;
+ }
+
+ /**
+ * Returns the hash of the filename of a resource
+ */
+ uint GetFileNameHash() const {
+ return _fileNameHash;
+ }
+
+ /**
+ * Returns a resource's type
+ */
+ uint GetType() const {
+ return _type;
+ }
+
+protected:
+ virtual ~Resource() {};
+
+private:
+ Common::String _fileName; ///< The absolute filename
+ uint _fileNameHash; ///< The hash value of the filename
+ uint _refCount; ///< The number of locks
+ uint _type; ///< The type of the resource
+ Common::List<Resource *>::iterator _iterator; ///< Points to the resource position in the LRU list
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/resservice.h b/engines/sword25/kernel/resservice.h
new file mode 100644
index 0000000000..d5961d52ae
--- /dev/null
+++ b/engines/sword25/kernel/resservice.h
@@ -0,0 +1,75 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_RESOURCESERVICE_H
+#define SWORD25_RESOURCESERVICE_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/resmanager.h"
+
+namespace Sword25 {
+
+class Resource;
+
+class ResourceService : public Service {
+public:
+ ResourceService(Kernel *pKernel) : Service(pKernel) {
+ ResourceManager *pResource = pKernel->GetResourceManager();
+ pResource->RegisterResourceService(this);
+ }
+
+ virtual ~ResourceService() {}
+
+
+ /**
+ * Loads a resource
+ * @return Returns the resource if successful, otherwise NULL
+ */
+ virtual Resource *loadResource(const Common::String &fileName) = 0;
+
+ /**
+ * Checks whether the given name can be loaded by the resource service
+ * @param FileName Dateiname
+ * @return Returns true if the resource can be loaded.
+ */
+ virtual bool canLoadResource(const Common::String &fileName) = 0;
+
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/scummvmwindow.cpp b/engines/sword25/kernel/scummvmwindow.cpp
new file mode 100644
index 0000000000..35fd27a05c
--- /dev/null
+++ b/engines/sword25/kernel/scummvmwindow.cpp
@@ -0,0 +1,297 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "common/system.h"
+#include "engines/util.h"
+#include "graphics/pixelformat.h"
+#include "sword25/kernel/scummvmwindow.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/input/inputengine.h"
+
+#define BS_LOG_PREFIX "WIN32WINDOW"
+
+namespace Sword25 {
+
+bool ScummVMWindow::_ClassRegistered = false;
+
+// Constructor / Destructor
+// ------------------------
+ScummVMWindow::ScummVMWindow(int X, int Y, int Width, int Height, bool Visible) {
+ // Presume that init will fail
+ _InitSuccess = false;
+
+ // We don't support any window creation except at the origin 0,0
+ assert(X == 0);
+ assert(Y == 0);
+
+ if (!_ClassRegistered) {
+ // Nothing here currently
+
+ _ClassRegistered = true;
+ }
+
+ // Fenstersichtbarkeit setzen
+ SetVisible(Visible);
+
+ // Indicate success
+ _InitSuccess = true;
+ _WindowAlive = true;
+ _CloseWanted = false;
+}
+
+ScummVMWindow::~ScummVMWindow() {
+}
+
+// Get Methods
+// ------------
+int ScummVMWindow::GetX() {
+ return 0;
+}
+
+int ScummVMWindow::GetY() {
+ return 0;
+}
+
+int ScummVMWindow::GetClientX() {
+ return 0;
+}
+
+int ScummVMWindow::GetClientY() {
+ return 0;
+}
+
+int ScummVMWindow::GetWidth() {
+ return g_system->getWidth();
+}
+
+int ScummVMWindow::GetHeight() {
+ return g_system->getHeight();
+}
+
+Common::String ScummVMWindow::GetTitle() {
+ return Common::String("");
+}
+
+bool ScummVMWindow::IsVisible() {
+ return true;
+}
+
+bool ScummVMWindow::HasFocus() {
+ // FIXME: Is there a way to tell if ScummVM has the focus in Windowed mode?
+ return true;
+}
+
+uint ScummVMWindow::GetWindowHandle() {
+ return 0;
+}
+
+void ScummVMWindow::SetWindowAlive(bool v) {
+ _WindowAlive = v;
+}
+
+
+// Set Methods
+// ------------
+
+void ScummVMWindow::SetX(int X) {
+ // No implementation
+}
+
+void ScummVMWindow::SetY(int Y) {
+ // No implementation
+}
+
+void ScummVMWindow::SetWidth(int Width) {
+ // No implementation
+}
+
+void ScummVMWindow::SetHeight(int Height) {
+ // No implementation
+}
+
+void ScummVMWindow::SetVisible(bool Visible) {
+ // No implementation
+}
+
+void ScummVMWindow::SetTitle(const Common::String &Title) {
+ // No implementation
+}
+
+bool ScummVMWindow::ProcessMessages() {
+ // All messages are handled separately in the input manager. The only thing we
+ // need to do here is to keep returning whether the window/game is still alive
+ return _WindowAlive;
+}
+
+bool ScummVMWindow::WaitForFocus() {
+ // No implementation
+ return true;
+}
+
+// FIXME: Special keys detected here need to be moved into the Input Engine
+/*
+// Die WindowProc aller Fenster der Klasse
+LRESULT CALLBACK BS_ScummVMWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_PAINT:
+ ValidateRect(hwnd, NULL);
+ break;
+
+ case WM_DESTROY:
+ // Das Fenster wird zerstört
+ PostQuitMessage(0);
+ break;
+
+ case WM_CLOSE:
+ {
+ BS_Window * WindowPtr = BS_Kernel::GetInstance()->GetWindow();
+ if (WindowPtr) {
+ WindowPtr->SetCloseWanted(true);
+ }
+ break;
+ }
+
+ case WM_KEYDOWN:
+ {
+ // Tastendrücke, die für das Inputmodul interessant sind, werden diesem gemeldet.
+ BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
+
+ if (InputPtr)
+ {
+ switch (wParam)
+ {
+ case VK_RETURN:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_ENTER);
+ break;
+
+ case VK_LEFT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_LEFT);
+ break;
+
+ case VK_RIGHT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_RIGHT);
+ break;
+
+ case VK_HOME:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_HOME);
+ break;
+
+ case VK_END:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_END);
+ break;
+
+ case VK_BACK:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_BACKSPACE);
+ break;
+
+ case VK_TAB:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_TAB);
+ break;
+
+ case VK_INSERT:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_INSERT);
+ break;
+
+ case VK_DELETE:
+ InputPtr->ReportCommand(BS_InputEngine::KEY_COMMAND_DELETE);
+ break;
+ }
+ }
+ break;
+ }
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ // Alle Tastendrücke werden ignoriert, damit Windows per DefWindowProc() nicht darauf
+ // reagieren kann und damit unerwartete Seiteneffekte auslöst.
+ // Zum Beispiel würden ALT und F10 Tastendrücke das "Menü" aktivieren und somit den Message-Loop zum Stillstand bringen.
+ break;
+
+ case WM_SYSCOMMAND:
+ // Verhindern, dass der Bildschirmschoner aktiviert wird, während das Spiel läuft
+ if (wParam != SC_SCREENSAVE) return DefWindowProc(hwnd,uMsg,wParam,lParam);
+ break;
+
+ case WM_CHAR:
+ {
+ byte theChar = static_cast<byte>(wParam & 0xff);
+
+ // Alle Zeichen, die keine Steuerzeichen sind, werden als Buchstaben dem Input-Service mitgeteilt.
+ if (theChar >= 32)
+ {
+ BS_InputEngine * InputPtr = BS_Kernel::GetInstance()->GetInput();
+ if (InputPtr) InputPtr->ReportCharacter(theChar);
+ }
+ }
+ break;
+
+ case WM_SETCURSOR:
+ {
+ // Der Systemcursor wird in der Client-Area des Fensters nicht angezeigt, jedoch in der nicht Client-Area, damit der Benutzer das Fenster wie gewohnt
+ // schließen und verschieben kann.
+
+ // Koordinaten des Cursors in der Client-Area berechnen.
+ POINT pt;
+ GetCursorPos(&pt);
+ ScreenToClient(hwnd, &pt);
+
+ // Feststellen, ob sich der Cursor in der Client-Area befindet.
+ // Get client rect
+ RECT rc;
+ GetClientRect(hwnd, &rc);
+
+ // See if cursor is in client area
+ if(PtInRect(&rc, pt))
+ // In der Client-Area keinen Cursor anzeigen.
+ SetCursor(NULL);
+ else
+ // Ausserhalb der Client-Area den Cursor anzeigen.
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ return TRUE;
+ }
+ break;
+
+ default:
+ // Um alle anderen Vorkommnisse kümmert sich Windows
+ return DefWindowProc(hwnd,uMsg,wParam,lParam);
+ }
+
+ return 0;
+}
+*/
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/scummvmwindow.h b/engines/sword25/kernel/scummvmwindow.h
new file mode 100644
index 0000000000..2b5f514b7d
--- /dev/null
+++ b/engines/sword25/kernel/scummvmwindow.h
@@ -0,0 +1,85 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_ScummVMWindow
+ ----------------
+ Implementation of the BS_Window Interfaces for ScummVM
+*/
+
+#ifndef SWORD25_SCUMMVMWINDOW_H
+#define SWORD25_SCUMMVMWINDOW_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/window.h"
+
+namespace Sword25 {
+
+// Class definition
+class ScummVMWindow : public Window {
+public:
+ ScummVMWindow(int X, int Y, int Width, int Height, bool Visible);
+ virtual ~ScummVMWindow();
+
+ bool IsVisible();
+ void SetVisible(bool Visible);
+ int GetX();
+ void SetX(int X);
+ int GetY();
+ void SetY(int X);
+ int GetClientX();
+ int GetClientY();
+ int GetWidth();
+ void SetWidth(int Width);
+ int GetHeight();
+ void SetHeight(int Height);
+ Common::String GetTitle();
+ void SetWindowAlive(bool v);
+ void SetTitle(const Common::String &Title);
+ bool HasFocus();
+ uint GetWindowHandle();
+ bool WaitForFocus();
+ bool ProcessMessages();
+
+private:
+ static bool _ClassRegistered;
+ bool _WindowAlive;
+ int _ClientXDelta;
+ int _ClientYDelta;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/service.h b/engines/sword25/kernel/service.h
new file mode 100644
index 0000000000..addcf50a08
--- /dev/null
+++ b/engines/sword25/kernel/service.h
@@ -0,0 +1,75 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_Service
+ * -------------
+ * This is the base class for all engine services.
+ * A serivce is an essential part of the engine, ex. the graphics system.
+ * This was intended to allow, for example, different plug in modules for
+ * different kinds of hardware and/or systems.
+ * The services are created at runtime via the kernel method NewService and NEVER with new.
+ *
+ * Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_SERVICE_H
+#define SWORD25_SERVICE_H
+
+// Includes
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// Klassendefinition
+class Kernel;
+
+class Service {
+private:
+ Kernel *_pKernel;
+
+protected:
+ Service(Kernel *pKernel) : _pKernel(pKernel) {};
+
+ Kernel *GetKernel() const {
+ return _pKernel;
+ }
+
+public:
+ virtual ~Service() {};
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/service_ids.h b/engines/sword25/kernel/service_ids.h
new file mode 100644
index 0000000000..5ffd83d743
--- /dev/null
+++ b/engines/sword25/kernel/service_ids.h
@@ -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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * service_ids.h
+ * -------------
+ * This file lists all the services.
+ * EVERY new service needs to be entered here, otherwise it cannot be instantiated
+ * by pKernel->NewService(..)
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_SERVICE_IDS
+#define SWORD25_SERVICE_IDS
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+Service *GraphicEngine_CreateObject(Kernel *pKernel);
+Service *PackageManager_CreateObject(Kernel *pKernel);
+Service *InputEngine_CreateObject(Kernel *pKernel);
+Service *SoundEngine_CreateObject(Kernel *pKernel);
+Service *LuaScriptEngine_CreateObject(Kernel *pKernel);
+Service *Geometry_CreateObject(Kernel *pKernel);
+Service *OggTheora_CreateObject(Kernel *pKernel);
+
+// Services are recorded in this table
+const BS_ServiceInfo BS_SERVICE_TABLE[] = {
+ // The first two parameters are the name of the superclass and service
+ // The third parameter is the static method of the class that creates an object
+ // of the class and returns it
+ // Example:
+ // BS_ServiceInfo("Superclass", "Service", CreateMethod)
+ BS_ServiceInfo("gfx", "opengl", GraphicEngine_CreateObject),
+ BS_ServiceInfo("package", "archiveFS", PackageManager_CreateObject),
+ BS_ServiceInfo("input", "winapi", InputEngine_CreateObject),
+ BS_ServiceInfo("sfx", "fmodex", SoundEngine_CreateObject),
+ BS_ServiceInfo("script", "lua", LuaScriptEngine_CreateObject),
+ BS_ServiceInfo("geometry", "std", Geometry_CreateObject),
+ BS_ServiceInfo("fmv", "oggtheora", OggTheora_CreateObject),
+};
+
+const uint BS_SERVICE_COUNT = sizeof(BS_SERVICE_TABLE) / sizeof(BS_ServiceInfo);
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/kernel/string.h b/engines/sword25/kernel/string.h
new file mode 100644
index 0000000000..b701e2312b
--- /dev/null
+++ b/engines/sword25/kernel/string.h
@@ -0,0 +1,111 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_STRING
+#define SWORD25_STRING
+
+#include "common/str.h"
+
+namespace BS_String {
+
+inline uint GetHash(const Common::String &Str) {
+ uint Result = 0;
+
+ for (uint i = 0; i < Str.size(); i++)
+ Result = ((Result << 5) - Result) + Str[i];
+
+ return Result;
+}
+
+inline bool ToInt(const Common::String &Str, int &Result) {
+ Common::String::const_iterator Iter = Str.begin();
+
+ // Skip whitespaces
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
+ ++Iter;
+ }
+ if (Iter == Str.end()) return false;
+
+ // Read sign, if available
+ bool IsNegative = false;
+ if (*Iter == '-') {
+ IsNegative = true;
+ ++Iter;
+ } else if (*Iter == '+')
+ ++Iter;
+
+ // Skip whitespaces
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
+ ++Iter;
+ }
+ if (Iter == Str.end()) return false;
+
+ // Convert string to integer
+ Result = 0;
+ while (Iter != Str.end()) {
+ if (*Iter < '0' || *Iter > '9') {
+ while (*Iter && (*Iter == ' ' || *Iter == '\t')) {
+ ++Iter;
+ }
+ if (Iter != Str.end()) return false;
+ break;
+ }
+ Result = (Result * 10) + (*Iter - '0');
+ ++Iter;
+ }
+
+ if (IsNegative) Result = -Result;
+
+ return true;
+}
+
+inline bool ToBool(const Common::String &Str, bool &Result) {
+ if (Str == "true" || Str == "TRUE") {
+ Result = true;
+ return true;
+ } else if (Str == "false" || Str == "FALSE") {
+ Result = false;
+ return true;
+ }
+
+ return false;
+}
+
+inline void ToLower(Common::String &Str) {
+ Str.toLowercase();
+}
+
+} // End of namespace BS_String
+
+#endif
diff --git a/engines/sword25/kernel/window.cpp b/engines/sword25/kernel/window.cpp
new file mode 100644
index 0000000000..8d2dc309e7
--- /dev/null
+++ b/engines/sword25/kernel/window.cpp
@@ -0,0 +1,69 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/window.h"
+
+// Alle Implementationen von BS_Window müssen hier eingetragen werden
+#include "sword25/kernel/scummvmwindow.h"
+
+namespace Sword25 {
+
+// Erstellt ein Fenster des GUI des aktuellen Betriebssystems
+Window *Window::CreateBSWindow(int X, int Y, int Width, int Height, bool Visible) {
+ // Fenster erstellen
+ Window *pWindow = (Window *) new ScummVMWindow(X, Y, Width, Height, Visible);
+
+ // Falls das Fenster erfolgreich initialisiert wurde, wird ein Pointer auf das Fensterobjekt
+ // zurückgegeben
+ if (pWindow->_InitSuccess)
+ return pWindow;
+
+ // Ansonsten wird das Fensterobjekt zerstört und NULL zurückgegeben
+ delete pWindow;
+ return NULL;
+}
+
+// Gibt True zurück wenn das Fenster WM_CLOSE empfangen hat -
+// solange, bis RejectClose() aufgerufen wurde.
+bool Window::CloseWanted() {
+ bool result = _CloseWanted;
+ _CloseWanted = false;
+ return result;
+}
+
+void Window::SetCloseWanted(bool Wanted) {
+ _CloseWanted = Wanted;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/kernel/window.h b/engines/sword25/kernel/window.h
new file mode 100644
index 0000000000..aee23087cb
--- /dev/null
+++ b/engines/sword25/kernel/window.h
@@ -0,0 +1,177 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_Window
+ * ---------
+ * Simple window class interface. This is being encapsulated in a class for
+ * reasons of portability.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_WINDOW_H
+#define SWORD25_WINDOW_H
+
+// Includes
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+// Class definitions
+
+/**
+ * A simple window class interface
+ *
+ * Windows are exclusively created by BS_Window::CreateBSWindow().
+ * BS_Windows selects the correct class for the environment.
+ */
+class Window {
+protected:
+ bool _InitSuccess;
+ bool _CloseWanted;
+
+public:
+ virtual ~Window() {};
+
+ /**
+ * Returns the visibility of the window.
+ */
+ virtual bool IsVisible() = 0;
+
+ /**
+ * Sets the visibility of the window
+ * @param Visible Specifies whether the window should be visible or hidden
+ */
+ virtual void SetVisible(bool Visible) = 0;
+ /**
+ * Returns the X position of the window
+ */
+ virtual int GetX() = 0;
+ /**
+ * Sets the X position of the window
+ * @paramX The new X position for the window, or -1 for centre aligned
+ */
+ virtual void SetX(int X) = 0;
+ /**
+ * Gets the Y position of the window
+ */
+ virtual int GetY() = 0;
+ /**
+ * Sets the Y position of the window
+ * @param Y The new Y position for the window, or -1 for centre aligned
+ */
+ virtual void SetY(int X) = 0;
+ /**
+ * Returns the X position of the window's client area
+ */
+ virtual int GetClientX() = 0;
+ /**
+ * Returns the Y position of the window's client area
+ */
+ virtual int GetClientY() = 0;
+ /**
+ * Returns the width of the window without the frame
+ */
+ virtual int GetWidth() = 0;
+ /**
+ * Sets the width of the window without the frame
+ */
+ virtual void SetWidth(int Width) = 0;
+ /**
+ * Gets the height of the window without the frame
+ */
+ virtual int GetHeight() = 0;
+ /**
+ * Sets the height of the window without the frame
+ */
+ virtual void SetHeight(int Height) = 0;
+ /**
+ * Returns the title of the window
+ */
+ virtual Common::String GetTitle() = 0;
+ /**
+ * Sets the title of the window
+ * @param Title The new window title
+ */
+ virtual void SetTitle(const Common::String &Title) = 0;
+ /**
+ * Handle the processing of any pending window messages. This method should be called
+ * during the main loop.
+ */
+ virtual bool ProcessMessages() = 0;
+ /**
+ * Pauses the applicaiton until the window has focus, or has been closed.
+ * Returns false if the window was closed.
+ */
+ virtual bool WaitForFocus() = 0;
+ /**
+ * Returns true if the window has focus, false otherwise.
+ */
+ virtual bool HasFocus() = 0;
+ /**
+ * Returns the system handle that represents the window. Note that any use of the handle
+ * will not be portable code.
+ */
+ virtual uint GetWindowHandle() = 0;
+
+ virtual void SetWindowAlive(bool v) = 0;
+
+ /**
+ * Specifies whether the window is wanted to be closed. This is used together with CloseWanted()
+ * to allow scripts to query when the main window should be closed, or the user is asking it to close
+ **/
+ void SetCloseWanted(bool Wanted);
+ /**
+ * Returns the previous value set in a call to SetCloseWanted.
+ * Note that calling this also resets the value back to false, until such time as the SetCloseWanted()
+ * method is called again.
+ **/
+ bool CloseWanted();
+
+ /**
+ * Creates a new window instance. Returns a pointer to the window, or NULL if the creation failed.
+ * Note: It is the responsibility of the client to free the pointer when done with it.
+ * @param X The X position of the window, or -1 for centre horizontal alignment
+ * @param Y The Y position of the window, or -1 for centre vertical alignment
+ * @param Width The width of the window without the frame
+ * @param Height The height of the window without the frame
+ * @param Visible Specifies whether window should be visible
+ */
+ static Window *CreateBSWindow(int X, int Y, int Width, int Height, bool Visible);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/geometry.cpp b/engines/sword25/math/geometry.cpp
new file mode 100644
index 0000000000..caa10160e9
--- /dev/null
+++ b/engines/sword25/math/geometry.cpp
@@ -0,0 +1,53 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/math/geometry.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "GEOMETRY"
+
+Geometry::Geometry(Kernel *pKernel) : Service(pKernel) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+
+Service *Geometry_CreateObject(Kernel *pKernel) {
+ return new Geometry(pKernel);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/geometry.h b/engines/sword25/math/geometry.h
new file mode 100644
index 0000000000..78aa30696e
--- /dev/null
+++ b/engines/sword25/math/geometry.h
@@ -0,0 +1,56 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_GEOMETRY_H
+#define SWORD25_GEOMETRY_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+
+namespace Sword25 {
+
+class Kernel;
+
+class Geometry : public Service {
+public:
+ Geometry(Kernel *pKernel);
+ virtual ~Geometry() {}
+
+private:
+ bool registerScriptBindings();
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/geometry_script.cpp b/engines/sword25/math/geometry_script.cpp
new file mode 100644
index 0000000000..dac766927b
--- /dev/null
+++ b/engines/sword25/math/geometry_script.cpp
@@ -0,0 +1,496 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "common/array.h"
+#include "sword25/gfx/graphicengine.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+
+#include "sword25/math/geometry.h"
+#include "sword25/math/region.h"
+#include "sword25/math/regionregistry.h"
+#include "sword25/math/walkregion.h"
+#include "sword25/math/vertex.h"
+
+namespace Sword25 {
+
+// These strings are defined as #defines to enable compile-time string composition
+#define REGION_CLASS_NAME "Geo.Region"
+#define WALKREGION_CLASS_NAME "Geo.WalkRegion"
+
+// How luaL_checkudata, only without that no error is generated.
+static void *my_checkudata(lua_State *L, int ud, const char *tname) {
+ int top = lua_gettop(L);
+
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ // lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ LuaBindhelper::getMetatable(L, tname);
+ /* does it have the correct mt? */
+ if (lua_rawequal(L, -1, -2)) {
+ lua_settop(L, top);
+ return p;
+ }
+ }
+ }
+
+ lua_settop(L, top);
+ return NULL;
+}
+
+static void newUintUserData(lua_State *L, uint value) {
+ void *userData = lua_newuserdata(L, sizeof(value));
+ memcpy(userData, &value, sizeof(value));
+}
+
+static bool isValidPolygonDefinition(lua_State *L) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Ensure that we actually consider a table
+ if (!lua_istable(L, -1)) {
+ luaL_error(L, "Invalid polygon definition. Unexpected type, \"table\" needed.");
+ return false;
+ }
+
+ int tableSize = luaL_getn(L, -1);
+
+ // Make sure that there are at least three Vertecies
+ if (tableSize < 6) {
+ luaL_error(L, "Invalid polygon definition. At least three vertecies needed.");
+ return false;
+ }
+
+ // Make sure that the number of table elements is divisible by two.
+ // Since any two elements is a vertex, an odd number of elements is not allowed
+ if ((tableSize % 2) != 0) {
+ luaL_error(L, "Invalid polygon definition. Even number of table elements needed.");
+ return false;
+ }
+
+ // Ensure that all elements in the table are of type Number
+ for (int i = 1; i <= tableSize; i++) {
+ lua_rawgeti(L, -1, i);
+ if (!lua_isnumber(L, -1)) {
+ luaL_error(L, "Invalid polygon definition. All table elements have to be numbers.");
+ return false;
+ }
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+static void tablePolygonToPolygon(lua_State *L, Polygon &polygon) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Ensure that a valid polygon definition is on the stack.
+ // It is not necessary to catch the return value, since all errors are reported on luaL_error
+ // End script.
+ isValidPolygonDefinition(L);
+
+ int vertexCount = luaL_getn(L, -1) / 2;
+
+ // Memory is reserved for Vertecies
+ Common::Array<Vertex> vertices;
+ vertices.reserve(vertexCount);
+
+ // Create Vertecies
+ for (int i = 0; i < vertexCount; i++) {
+ // X Value
+ lua_rawgeti(L, -1, (i * 2) + 1);
+ int X = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Y Value
+ lua_rawgeti(L, -1, (i * 2) + 2);
+ int Y = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Vertex
+ vertices.push_back(Vertex(X, Y));
+ }
+ BS_ASSERT((int)vertices.size() == vertexCount);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ // Create polygon
+ polygon.init(vertexCount, &vertices[0]);
+}
+
+static uint tableRegionToRegion(lua_State *L, const char *className) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // You can define a region in Lua in two ways:
+ // 1. A table that defines a polygon (polgon = table with numbers, which define
+ // two consecutive numbers per vertex)
+ // 2. A table containing more polygon definitions
+ // Then the first polygon is the contour of the region, and the following are holes
+ // defined in the first polygon.
+
+ // It may be passed only one parameter, and this must be a table
+ if (lua_gettop(L) != 1 || !lua_istable(L, -1)) {
+ luaL_error(L, "First and only parameter has to be of type \"table\".");
+ return 0;
+ }
+
+ uint regionHandle = 0;
+ if (!strcmp(className, REGION_CLASS_NAME)) {
+ regionHandle = Region::create(Region::RT_REGION);
+ } else if (!strcmp(className, WALKREGION_CLASS_NAME)) {
+ regionHandle = WalkRegion::create(Region::RT_WALKREGION);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ BS_ASSERT(regionHandle);
+
+ // If the first element of the parameter is a number, then case 1 is accepted
+ // If the first element of the parameter is a table, then case 2 is accepted
+ // If the first element of the parameter has a different type, there is an error
+ lua_rawgeti(L, -1, 1);
+ int firstElementType = lua_type(L, -1);
+ lua_pop(L, 1);
+
+ switch (firstElementType) {
+ case LUA_TNUMBER: {
+ Polygon polygon;
+ tablePolygonToPolygon(L, polygon);
+ RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon);
+ }
+ break;
+
+ case LUA_TTABLE: {
+ lua_rawgeti(L, -1, 1);
+ Polygon polygon;
+ tablePolygonToPolygon(L, polygon);
+ lua_pop(L, 1);
+
+ int polygonCount = luaL_getn(L, -1);
+ if (polygonCount == 1)
+ RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon);
+ else {
+ Common::Array<Polygon> holes;
+ holes.reserve(polygonCount - 1);
+
+ for (int i = 2; i <= polygonCount; i++) {
+ lua_rawgeti(L, -1, i);
+ holes.resize(holes.size() + 1);
+ tablePolygonToPolygon(L, holes.back());
+ lua_pop(L, 1);
+ }
+ BS_ASSERT((int)holes.size() == polygonCount - 1);
+
+ RegionRegistry::getInstance().resolveHandle(regionHandle)->init(polygon, &holes);
+ }
+ }
+ break;
+
+ default:
+ luaL_error(L, "Illegal region definition.");
+ return 0;
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return regionHandle;
+}
+
+static void newUserdataRegion(lua_State *L, const char *className) {
+ // Region due to the Lua code to create
+ // Any errors that occur will be intercepted to the luaL_error
+ uint regionHandle = tableRegionToRegion(L, className);
+ BS_ASSERT(regionHandle);
+
+ newUintUserData(L, regionHandle);
+ // luaL_getmetatable(L, className);
+ LuaBindhelper::getMetatable(L, className);
+ BS_ASSERT(!lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+}
+
+static int newRegion(lua_State *L) {
+ newUserdataRegion(L, REGION_CLASS_NAME);
+ return 1;
+}
+
+static int newWalkRegion(lua_State *L) {
+ newUserdataRegion(L, WALKREGION_CLASS_NAME);
+ return 1;
+}
+
+static const char *GEO_LIBRARY_NAME = "Geo";
+
+static const luaL_reg GEO_FUNCTIONS[] = {
+ {"NewRegion", newRegion},
+ {"NewWalkRegion", newWalkRegion},
+ {0, 0}
+};
+
+static Region *checkRegion(lua_State *L) {
+ // The first parameter must be of type 'userdata', and the Metatable class Geo.Region or Geo.WalkRegion
+ uint *regionHandlePtr;
+ if ((regionHandlePtr = reinterpret_cast<uint *>(my_checkudata(L, 1, REGION_CLASS_NAME))) != 0 ||
+ (regionHandlePtr = reinterpret_cast<uint *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
+ return RegionRegistry::getInstance().resolveHandle(*regionHandlePtr);
+ } else {
+ luaL_argcheck(L, 0, 1, "'" REGION_CLASS_NAME "' expected");
+ }
+
+ // Compilation fix. Execution never reaches this point
+ return 0;
+}
+
+static int r_isValid(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushbooleancpp(L, pR->isValid());
+ return 1;
+}
+
+static int r_getX(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushnumber(L, pR->getPosX());
+ return 1;
+}
+
+static int r_getY(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ lua_pushnumber(L, pR->getPosY());
+ return 1;
+}
+
+static int r_getPos(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ Vertex::vertexToLuaVertex(L, pR->getPosition());
+ return 1;
+}
+
+static int r_isPointInRegion(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ Vertex vertex;
+ Vertex::luaVertexToVertex(L, 2, vertex);
+ lua_pushbooleancpp(L, pR->isPointInRegion(vertex));
+ return 1;
+}
+
+static int r_setPos(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ Vertex vertex;
+ Vertex::luaVertexToVertex(L, 2, vertex);
+ pR->setPos(vertex.x, vertex.y);
+
+ return 0;
+}
+
+static int r_setX(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ pR->setPosX(static_cast<int>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+static int r_setY(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ pR->setPosY(static_cast<int>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+static void drawPolygon(const Polygon &polygon, uint color, const Vertex &offset) {
+ GraphicEngine *pGE = static_cast<GraphicEngine *>(Kernel::GetInstance()->GetService("gfx"));
+ BS_ASSERT(pGE);
+
+ for (int i = 0; i < polygon.vertexCount - 1; i++)
+ pGE->DrawDebugLine(polygon.vertices[i] + offset, polygon.vertices[i + 1] + offset, color);
+
+ pGE->DrawDebugLine(polygon.vertices[polygon.vertexCount - 1] + offset, polygon.vertices[0] + offset, color);
+}
+
+static void drawRegion(const Region &region, uint color, const Vertex &offset) {
+ drawPolygon(region.getContour(), color, offset);
+ for (int i = 0; i < region.getHoleCount(); i++)
+ drawPolygon(region.getHole(i), color, offset);
+}
+
+static int r_draw(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+
+ switch (lua_gettop(L)) {
+ case 3: {
+ Vertex offset;
+ Vertex::luaVertexToVertex(L, 3, offset);
+ drawRegion(*pR, GraphicEngine::LuaColorToARGBColor(L, 2), offset);
+ }
+ break;
+
+ case 2:
+ drawRegion(*pR, GraphicEngine::LuaColorToARGBColor(L, 2), Vertex(0, 0));
+ break;
+
+ default:
+ drawRegion(*pR, BS_RGB(255, 255, 255), Vertex(0, 0));
+ }
+
+ return 0;
+}
+
+static int r_getCentroid(lua_State *L) {
+ Region *RPtr = checkRegion(L);
+ BS_ASSERT(RPtr);
+
+ Vertex::vertexToLuaVertex(L, RPtr->getCentroid());
+
+ return 1;
+}
+
+static int r_delete(lua_State *L) {
+ Region *pR = checkRegion(L);
+ BS_ASSERT(pR);
+ delete pR;
+ return 0;
+}
+
+static const luaL_reg REGION_METHODS[] = {
+ {"SetPos", r_setPos},
+ {"SetX", r_setX},
+ {"SetY", r_setY},
+ {"GetPos", r_getPos},
+ {"IsPointInRegion", r_isPointInRegion},
+ {"GetX", r_getX},
+ {"GetY", r_getY},
+ {"IsValid", r_isValid},
+ {"Draw", r_draw},
+ {"GetCentroid", r_getCentroid},
+ {0, 0}
+};
+
+static WalkRegion *checkWalkRegion(lua_State *L) {
+ // The first parameter must be of type 'userdate', and the Metatable class Geo.WalkRegion
+ uint regionHandle;
+ if ((regionHandle = *reinterpret_cast<uint *>(my_checkudata(L, 1, WALKREGION_CLASS_NAME))) != 0) {
+ return reinterpret_cast<WalkRegion *>(RegionRegistry::getInstance().resolveHandle(regionHandle));
+ } else {
+ luaL_argcheck(L, 0, 1, "'" WALKREGION_CLASS_NAME "' expected");
+ }
+
+ // Compilation fix. Execution never reaches this point
+ return 0;
+}
+
+static int wr_getPath(lua_State *L) {
+ WalkRegion *pWR = checkWalkRegion(L);
+ BS_ASSERT(pWR);
+
+ Vertex start;
+ Vertex::luaVertexToVertex(L, 2, start);
+ Vertex end;
+ Vertex::luaVertexToVertex(L, 3, end);
+ BS_Path path;
+ if (pWR->queryPath(start, end, path)) {
+ lua_newtable(L);
+ BS_Path::const_iterator it = path.begin();
+ for (; it != path.end(); it++) {
+ lua_pushnumber(L, (it - path.begin()) + 1);
+ Vertex::vertexToLuaVertex(L, *it);
+ lua_settable(L, -3);
+ }
+ } else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static const luaL_reg WALKREGION_METHODS[] = {
+ {"GetPath", wr_getPath},
+ {0, 0}
+};
+
+bool Geometry::registerScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast< lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addMethodsToClass(L, REGION_CLASS_NAME, REGION_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, WALKREGION_CLASS_NAME, REGION_METHODS)) return false;
+ if (!LuaBindhelper::addMethodsToClass(L, WALKREGION_CLASS_NAME, WALKREGION_METHODS)) return false;
+
+ if (!LuaBindhelper::setClassGCHandler(L, REGION_CLASS_NAME, r_delete)) return false;
+ if (!LuaBindhelper::setClassGCHandler(L, WALKREGION_CLASS_NAME, r_delete)) return false;
+
+ if (!LuaBindhelper::addFunctionsToLib(L, GEO_LIBRARY_NAME, GEO_FUNCTIONS)) return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/line.h b/engines/sword25/math/line.h
new file mode 100644
index 0000000000..d57fce68f7
--- /dev/null
+++ b/engines/sword25/math/line.h
@@ -0,0 +1,198 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_Line
+ -------
+ This class contains only static methods, which have to do with straight line
+ segments. There is no real straight line segment class. Calculations will be
+ used with polygons, and it is important the process of starting and selecting
+ endpoints of lines is dynamic. This would prhobit a polygon from a set
+ being formed by fixed line segments
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_LINE_H
+#define SWORD25_LINE_H
+
+#include "sword25/kernel/common.h"
+
+namespace Sword25 {
+
+class Line {
+public:
+ /**
+ * Determines whether a piont is left of a line
+ * @param a The start point of a line
+ * @param b The end point of a line
+ * @param c The test point
+ * @return Returns true if the point is to the left of the line.
+ * If the point is to the right of the line or on the line, false is returned.
+ */
+ static bool isVertexLeft(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return triangleArea2(a, b, c) > 0;
+ }
+
+ static bool isVertexLeftOn(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return triangleArea2(a, b, c) >= 0;
+ }
+
+ /**
+ * Determines whether a piont is right of a line
+ * @param a The start point of a line
+ * @param b The end point of a line
+ * @param c The test point
+ * @return Returns true if the point is to the right of the line.
+ * If the point is to the right of the line or on the line, false is returned.
+ */
+ static bool isVertexRight(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return triangleArea2(a, b, c) < 0;
+ }
+
+ static bool isVertexRightOn(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return triangleArea2(a, b, c) <= 0;
+ }
+
+ /**
+ * Determines whether a piont is on a line
+ * @param a The start point of a line
+ * @param b The end point of a line
+ * @param c The test point
+ * @return Returns true if the point is on the line, false otherwise.
+ */
+ static bool isVertexOn(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return triangleArea2(a, b, c) == 0;
+ }
+
+ enum VERTEX_CLASSIFICATION {
+ LEFT,
+ RIGHT,
+ ON
+ };
+
+ /**
+ * Determines where a point is relative to a line.
+ * @param a The start point of a line
+ * @param b The end point of a line
+ * @param c The test point
+ * @return LEFT is returned if the point is to the left of the line.
+ * RIGHT is returned if the point is to the right of the line.
+ * ON is returned if the point is on the line.
+ */
+ static VERTEX_CLASSIFICATION classifyVertexToLine(const Vertex &a, const Vertex &b, const Vertex &c) {
+ int area = triangleArea2(a, b, c);
+ if (area > 0) return LEFT;
+ if (area < 0) return RIGHT;
+ return ON;
+ }
+
+ /**
+ * Determines whether two lines intersect
+ * @param a The start point of the first line
+ * @param b The end point of the first line
+ * @param c The start point of the second line
+ * @param d The end point of the second line
+ * @remark In cases where a line only touches the other, false is returned (improper intersection)
+ */
+ static bool doesIntersectProperly(const Vertex &a, const Vertex &b, const Vertex &c, const Vertex &d) {
+ VERTEX_CLASSIFICATION class1 = classifyVertexToLine(a, b, c);
+ VERTEX_CLASSIFICATION class2 = classifyVertexToLine(a, b, d);
+ VERTEX_CLASSIFICATION class3 = classifyVertexToLine(c, d, a);
+ VERTEX_CLASSIFICATION class4 = classifyVertexToLine(c, d, b);
+
+ if (class1 == ON || class2 == ON || class3 == ON || class4 == ON) return false;
+
+ return ((class1 == LEFT) ^(class2 == LEFT)) && ((class3 == LEFT) ^(class4 == LEFT));
+ }
+
+ /**
+ * Determines whether a point is on a line segment
+ * @param a The start point of a line
+ * @param b The end point of a line
+ * @param c The test point
+ */
+ static bool isOnLine(const Vertex &a, const Vertex &b, const Vertex &c) {
+ // The items must all be Collinear, otherwise don't bothering testing the point
+ if (triangleArea2(a, b, c) != 0) return false;
+
+ // If the line segment is not vertical, check on the x-axis, otherwise the y-axis
+ if (a.x != b.x) {
+ return ((a.x <= c.x) &&
+ (c.x <= b.x)) ||
+ ((a.x >= c.x) &&
+ (c.x >= b.x));
+ } else {
+ return ((a.y <= c.y) &&
+ (c.y <= b.y)) ||
+ ((a.y >= c.y) &&
+ (c.y >= b.y));
+ }
+ }
+
+ static bool isOnLineStrict(const Vertex &a, const Vertex &b, const Vertex &c) {
+ // The items must all be Collinear, otherwise don't bothering testing the point
+ if (triangleArea2(a, b, c) != 0) return false;
+
+ // If the line segment is not vertical, check on the x-axis, otherwise the y-axis
+ if (a.x != b.x) {
+ return ((a.x < c.x) &&
+ (c.x < b.x)) ||
+ ((a.x > c.x) &&
+ (c.x > b.x));
+ } else {
+ return ((a.y < c.y) &&
+ (c.y < b.y)) ||
+ ((a.y > c.y) &&
+ (c.y > b.y));
+ }
+ }
+
+private:
+ /**
+ * Return double the size of the triangle defined by the three passed points.
+ *
+ * The result is positive if the points are arrange counterclockwise,
+ * and negative if they are arranged counter-clockwise.
+ */
+ static int triangleArea2(const Vertex &a, const Vertex &b, const Vertex &c) {
+ return a.x * b.y - a.y * b.x +
+ a.y * c.x - a.x * c.y +
+ b.x * c.y - c.x * b.y;
+ }
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/polygon.cpp b/engines/sword25/math/polygon.cpp
new file mode 100644
index 0000000000..700c375e8c
--- /dev/null
+++ b/engines/sword25/math/polygon.cpp
@@ -0,0 +1,491 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include <math.h>
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+#include "sword25/math/polygon.h"
+#include "sword25/math/line.h"
+
+namespace Sword25 {
+
+Polygon::Polygon() : vertexCount(0), vertices(NULL) {
+}
+
+Polygon::Polygon(int vertexCount_, const Vertex *vertices_) : vertexCount(0), vertices(NULL) {
+ init(vertexCount_, vertices_);
+}
+
+Polygon::Polygon(const Polygon &other) : vertexCount(0), vertices(NULL) {
+ init(other.vertexCount, other.vertices);
+}
+
+Polygon::Polygon(InputPersistenceBlock &Reader) : vertexCount(0), vertices(NULL) {
+ unpersist(Reader);
+}
+
+Polygon::~Polygon() {
+ delete[] vertices;
+}
+
+bool Polygon::init(int vertexCount_, const Vertex *vertices_) {
+ // Rember the old obstate to restore it if an error occurs whilst initialising it with the new data
+ int oldvertexCount = this->vertexCount;
+ Vertex *oldvertices = this->vertices;
+
+ this->vertexCount = vertexCount_;
+ this->vertices = new Vertex[vertexCount_ + 1];
+ memcpy(this->vertices, vertices_, sizeof(Vertex) * vertexCount_);
+ // TODO:
+ // Duplicate and remove redundant vertecies (Superflous = 3 co-linear verts)
+ // _WeedRepeatedvertices();
+ // The first vertex is repeated at the end of the vertex array; this simplifies
+ // some algorithms, running through the edges and thus can save the overflow control.
+ this->vertices[vertexCount_] = this->vertices[0];
+
+ // If the polygon is self-intersecting, the object state is restore, and an error signalled
+ if (checkForSelfIntersection()) {
+ delete[] this->vertices;
+ this->vertices = oldvertices;
+ this->vertexCount = oldvertexCount;
+
+ // BS_LOG_ERROR("POLYGON: Tried to create a self-intersecting polygon.\n");
+ return false;
+ }
+
+ // Release old vertex list
+ delete[] oldvertices;
+
+ // Calculate properties of the polygon
+ _isCW = computeIsCW();
+ _isConvex = computeIsConvex();
+ _centroid = computeCentroid();
+
+ return true;
+}
+
+// Review the order of the vertices
+// ---------------------------------
+
+bool Polygon::isCW() const {
+ return _isCW;
+}
+
+bool Polygon::isCCW() const {
+ return !isCW();
+}
+
+bool Polygon::computeIsCW() const {
+ if (vertexCount) {
+ // Find the vertex on extreme bottom right
+ int v2Index = findLRVertexIndex();
+
+ // Find the vertex before and after it
+ int v1Index = (v2Index + (vertexCount - 1)) % vertexCount;
+ int v3Index = (v2Index + 1) % vertexCount;
+
+ // Cross product form
+ // If the cross product of the vertex lying fartherest bottom left is positive,
+ // the vertecies arrranged in a clockwise order. Otherwise counter-clockwise
+ if (crossProduct(vertices[v1Index], vertices[v2Index], vertices[v3Index]) >= 0)
+ return true;
+ }
+
+ return false;
+}
+
+int Polygon::findLRVertexIndex() const {
+ if (vertexCount) {
+ int curIndex = 0;
+ int maxX = vertices[0].x;
+ int maxY = vertices[0].y;
+
+ for (int i = 1; i < vertexCount; i++) {
+ if (vertices[i].y > maxY ||
+ (vertices[i].y == maxY && vertices[i].x > maxX)) {
+ maxX = vertices[i].x;
+ maxY = vertices[i].y;
+ curIndex = i;
+ }
+ }
+
+ return curIndex;
+ }
+
+ return -1;
+}
+
+// Testing for Convex / Concave
+// ------------------------
+
+bool Polygon::isConvex() const {
+ return _isConvex;
+}
+
+bool Polygon::isConcave() const {
+ return !isConvex();
+}
+
+bool Polygon::computeIsConvex() const {
+ // Polygons with three or less vertices can only be convex
+ if (vertexCount <= 3) return true;
+
+ // All angles in the polygon computed will have the same direction sign if the polygon is convex
+ int flag = 0;
+ for (int i = 0; i < vertexCount; i++) {
+ // Determine the next two vertecies to check
+ int j = (i + 1) % vertexCount;
+ int k = (i + 2) % vertexCount;
+
+ // Calculate the cross product of the three vertecies
+ int cross = crossProduct(vertices[i], vertices[j], vertices[k]);
+
+ // The lower two bits of the flag represent the following:
+ // 0: negative angle occurred
+ // 1: positive angle occurred
+
+ // The sign of the current angle is recorded in Flag
+ if (cross < 0)
+ flag |= 1;
+ else if (cross > 0)
+ flag |= 2;
+
+ // If flag is 3, there are both positive and negative angles; so the polygon is concave
+ if (flag == 3)
+ return false;
+ }
+
+ // Polygon is convex
+ return true;
+}
+
+// Make a determine vertex order
+// -----------------------------
+
+void Polygon::ensureCWOrder() {
+ if (!isCW())
+ reverseVertexOrder();
+}
+
+void Polygon::ensureCCWOrder() {
+ if (!isCCW())
+ reverseVertexOrder();
+}
+
+// Reverse the order of vertecies
+// ------------------------------
+
+void Polygon::reverseVertexOrder() {
+ // vertices are exchanged in pairs, until the list has been completely reversed
+ for (int i = 0; i < vertexCount / 2; i++) {
+ Vertex tempVertex = vertices[i];
+ vertices[i] = vertices[vertexCount - i - 1];
+ vertices[vertexCount - i - 1] = tempVertex;
+ }
+
+ // Vertexordnung neu berechnen.
+ _isCW = computeIsCW();
+}
+
+// Cross Product
+// -------------
+
+int Polygon::crossProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const {
+ return (v2.x - v1.x) * (v3.y - v2.y) -
+ (v2.y - v1.y) * (v3.x - v2.x);
+}
+
+// Scalar Product
+// --------------
+
+int Polygon::dotProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const {
+ return (v1.x - v2.x) * (v3.x - v2.x) +
+ (v1.y - v2.y) * (v3.x - v2.y);
+}
+
+// Check for self-intersections
+// ----------------------------
+
+bool Polygon::checkForSelfIntersection() const {
+ // TODO: Finish this
+ /*
+ float AngleSum = 0.0f;
+ for (int i = 0; i < vertexCount; i++) {
+ int j = (i + 1) % vertexCount;
+ int k = (i + 2) % vertexCount;
+
+ float Dot = DotProduct(vertices[i], vertices[j], vertices[k]);
+
+ // Skalarproduct normalisieren
+ float Length1 = sqrt((vertices[i].x - vertices[j].x) * (vertices[i].x - vertices[j].x) +
+ (vertices[i].y - vertices[j].y) * (vertices[i].y - vertices[j].y));
+ float Length2 = sqrt((vertices[k].x - vertices[j].x) * (vertices[k].x - vertices[j].x) +
+ (vertices[k].y - vertices[j].y) * (vertices[k].y - vertices[j].y));
+ float Norm = Length1 * Length2;
+
+ if (Norm > 0.0f) {
+ Dot /= Norm;
+ AngleSum += acos(Dot);
+ }
+ }
+ */
+
+ return false;
+}
+
+// Move
+// ----
+
+void Polygon::operator+=(const Vertex &delta) {
+ // Move all vertecies
+ for (int i = 0; i < vertexCount; i++)
+ vertices[i] += delta;
+
+ // Shift the focus
+ _centroid += delta;
+}
+
+// Line of Sight
+// -------------
+
+bool Polygon::isLineInterior(const Vertex &a, const Vertex &b) const {
+ // Both points have to be in the polygon
+ if (!isPointInPolygon(a, true) || !isPointInPolygon(b, true))
+ return false;
+
+ // If the points are identical, the line is trivially within the polygon
+ if (a == b)
+ return true;
+
+ // Test whether the line intersects a line segment strictly (proper intersection)
+ for (int i = 0; i < vertexCount; i++) {
+ int j = (i + 1) % vertexCount;
+ const Vertex &vs = vertices[i];
+ const Vertex &ve = vertices[j];
+
+ // If the line intersects a line segment strictly (proper cross section) the line is not in the polygon
+ if (Line::doesIntersectProperly(a, b, vs, ve))
+ return false;
+
+ // If one of the two line items is on the edge and the other is to the right of the edge,
+ // then the line is not completely within the polygon
+ if (Line::isOnLineStrict(vs, ve, a) && Line::isVertexRight(vs, ve, b))
+ return false;
+ if (Line::isOnLineStrict(vs, ve, b) && Line::isVertexRight(vs, ve, a))
+ return false;
+
+ // If one of the two line items is on a vertex, the line traces into the polygon
+ if ((a == vs) && !isLineInCone(i, b, true))
+ return false;
+ if ((b == vs) && !isLineInCone(i, a, true))
+ return false;
+ }
+
+ return true;
+}
+
+bool Polygon::isLineExterior(const Vertex &a, const Vertex &b) const {
+ // Neither of the two points must be strictly in the polygon (on the edge is allowed)
+ if (isPointInPolygon(a, false) || isPointInPolygon(b, false))
+ return false;
+
+ // If the points are identical, the line is trivially outside of the polygon
+ if (a == b)
+ return true;
+
+ // Test whether the line intersects a line segment strictly (proper intersection)
+ for (int i = 0; i < vertexCount; i++) {
+ int j = (i + 1) % vertexCount;
+ const Vertex &vs = vertices[i];
+ const Vertex &ve = vertices[j];
+
+ // If the line intersects a line segment strictly (proper intersection), then
+ // the line is partially inside the polygon
+ if (Line::doesIntersectProperly(a, b, vs, ve))
+ return false;
+
+ // If one of the two line items is on the edge and the other is to the right of the edge,
+ // the line is not completely outside the polygon
+ if (Line::isOnLineStrict(vs, ve, a) && Line::isVertexLeft(vs, ve, b))
+ return false;
+ if (Line::isOnLineStrict(vs, ve, b) && Line::isVertexLeft(vs, ve, a))
+ return false;
+
+ // If one of the lwo line items is on a vertex, the line must not run into the polygon
+ if ((a == vs) && isLineInCone(i, b, false))
+ return false;
+ if ((b == vs) && isLineInCone(i, a, false))
+ return false;
+
+ // If the vertex with start and end point is collinear, (a vs) and (b, vs) is not in the polygon
+ if (Line::isOnLine(a, b, vs)) {
+ if (isLineInCone(i, a, false))
+ return false;
+ if (isLineInCone(i, b, false))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Polygon::isLineInCone(int startVertexIndex, const Vertex &endVertex, bool includeEdges) const {
+ const Vertex &startVertex = vertices[startVertexIndex];
+ const Vertex &nextVertex = vertices[(startVertexIndex + 1) % vertexCount];
+ const Vertex &prevVertex = vertices[(startVertexIndex + vertexCount - 1) % vertexCount];
+
+ if (Line::isVertexLeftOn(prevVertex, startVertex, nextVertex)) {
+ if (includeEdges)
+ return Line::isVertexLeftOn(endVertex, startVertex, nextVertex) &&
+ Line::isVertexLeftOn(startVertex, endVertex, prevVertex);
+ else
+ return Line::isVertexLeft(endVertex, startVertex, nextVertex) &&
+ Line::isVertexLeft(startVertex, endVertex, prevVertex);
+ } else {
+ if (includeEdges)
+ return !(Line::isVertexLeft(endVertex, startVertex, prevVertex) &&
+ Line::isVertexLeft(startVertex, endVertex, nextVertex));
+ else
+ return !(Line::isVertexLeftOn(endVertex, startVertex, prevVertex) &&
+ Line::isVertexLeftOn(startVertex, endVertex, nextVertex));
+ }
+}
+
+// Point-Polygon Tests
+// -------------------
+
+bool Polygon::isPointInPolygon(int x, int y, bool borderBelongsToPolygon) const {
+ return isPointInPolygon(Vertex(x, y), borderBelongsToPolygon);
+}
+
+bool Polygon::isPointInPolygon(const Vertex &point, bool edgesBelongToPolygon) const {
+ int rcross = 0; // Number of right-side overlaps
+ int lcross = 0; // Number of left-side overlaps
+
+ // Each edge is checked whether it cuts the outgoing stream from the point
+ for (int i = 0; i < vertexCount; i++) {
+ const Vertex &edgeStart = vertices[i];
+ const Vertex &edgeEnd = vertices[(i + 1) % vertexCount];
+
+ // A vertex is a point? Then it lies on one edge of the polygon
+ if (point == edgeStart)
+ return edgesBelongToPolygon;
+
+ if ((edgeStart.y > point.y) != (edgeEnd.y > point.y)) {
+ int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
+ int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
+ if ((term1 > 0) == (term2 >= 0))
+ rcross++;
+ }
+
+ if ((edgeStart.y < point.y) != (edgeEnd.y < point.y)) {
+ int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
+ int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
+ if ((term1 < 0) == (term2 <= 0))
+ lcross++;
+ }
+ }
+
+ // The point is on an adge, if the number of left and right intersections have the same even numbers
+ if ((rcross % 2) != (lcross % 2))
+ return edgesBelongToPolygon;
+
+ // The point is strictly inside the polygon if and only if the number of overlaps is odd
+ if ((rcross % 2) == 1)
+ return true;
+ else
+ return false;
+}
+
+bool Polygon::persist(OutputPersistenceBlock &writer) {
+ writer.write(vertexCount);
+ for (int i = 0; i < vertexCount; ++i) {
+ writer.write(vertices[i].x);
+ writer.write(vertices[i].y);
+ }
+
+ return true;
+}
+
+bool Polygon::unpersist(InputPersistenceBlock &reader) {
+ int storedvertexCount;
+ reader.read(storedvertexCount);
+
+ Common::Array<Vertex> storedvertices;
+ for (int i = 0; i < storedvertexCount; ++i) {
+ int x, y;
+ reader.read(x);
+ reader.read(y);
+ storedvertices.push_back(Vertex(x, y));
+ }
+
+ init(storedvertexCount, &storedvertices[0]);
+
+ return reader.isGood();
+}
+
+// Main Focus
+// ----------
+
+Vertex Polygon::getCentroid() const {
+ return _centroid;
+}
+
+Vertex Polygon::computeCentroid() const {
+ // Area of the polygon is calculated
+ int doubleArea = 0;
+ for (int i = 0; i < vertexCount; ++i) {
+ doubleArea += vertices[i].x * vertices[i + 1].y - vertices[i + 1].x * vertices[i].y;
+ }
+
+ // Avoid division by zero in the next step
+ if (doubleArea == 0)
+ return Vertex();
+
+ // Calculate centroid
+ Vertex centroid;
+ for (int i = 0; i < vertexCount; ++i) {
+ int area = vertices[i].x * vertices[i + 1].y - vertices[i + 1].x * vertices[i].y;
+ centroid.x += (vertices[i].x + vertices[i + 1].x) * area;
+ centroid.y += (vertices[i].y + vertices[i + 1].y) * area;
+ }
+ centroid.x /= 3 * doubleArea;
+ centroid.y /= 3 * doubleArea;
+
+ return centroid;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/polygon.h b/engines/sword25/math/polygon.h
new file mode 100644
index 0000000000..3bd243f666
--- /dev/null
+++ b/engines/sword25/math/polygon.h
@@ -0,0 +1,262 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_POLYGON_H
+#define SWORD25_POLYGON_H
+
+// Includes
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/math/vertex.h"
+
+namespace Sword25 {
+
+class Vertex;
+
+/**
+ @brief Eine Polygonklasse.
+*/
+class Polygon : public Persistable {
+public:
+ /**
+ * Creates an object of type #BS_Polygon, containing 0 Vertecies.
+ *
+ * With the method Init(), Vertices can be added in later
+ */
+ Polygon();
+
+ /**
+ * Copy constructor
+ */
+ Polygon(const Polygon &other);
+
+ /**
+ * Creates a polygon using persisted data
+ */
+ Polygon(InputPersistenceBlock &reader);
+
+ /**
+ * Creaes an object of type #BS_Polygon, and assigns Vertices to it
+ * @param VertexCount The number of vertices being passed
+ * @param Vertecies An array of BS_Vertex objects representing the vertices in the polygon.
+ * @remark The Vertecies that define a polygon must not have any self-intersections.
+ * If the polygon does have self-intersections, then an empty polygon object is created.
+ */
+ Polygon(int vertexCount_, const Vertex *vertices_);
+
+ /**
+ * Deletes the BS_Polygon object
+ */
+ virtual ~Polygon();
+
+ /**
+ * Initialises the BS_Polygon with a list of Vertecies.
+ *
+ * The Vertices need to define a polygon must not have self-intersections.
+ * If a polygon already has verticies, this will re-initialise it with the new list.
+ *
+ * @param VertexCount The number of vertices being passed
+ * @param Vertecies An array of BS_Vertex objects representing the vertices in the polygon.
+ * @return Returns false if the Vertecies have self-intersections. In this case,
+ * the object is not initialised.
+ */
+ bool init(int vertexCount_, const Vertex *vertices_);
+
+ //
+ // ** Exploratory methods **
+ //
+
+ /**
+ * Checks whether the Vertecies of the polygon are arranged in a clockwise direction.
+ * @return Returns true if the Vertecies of the polygon are arranged clockwise or co-planar.
+ * Returns false if the Vertecies of the polygon are arrange counter-clockwise.
+ * @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
+ */
+ bool isCW() const;
+
+ /**
+ * Checks whether the Vertices of the polygon are arranged in a counter-clockwise direction.
+ * @return Returns true if the Vertecies of the polygon are arranged counter-clockwise.
+ * Returns false if the Vertecies of the polygon are arranged clockwise or co-planar.
+ * @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
+ */
+ bool isCCW() const;
+
+ /**
+ * Checks whether the polygon is convex.
+ * @return Returns true if the polygon is convex. Returns false if the polygon is concave.
+ * @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
+ */
+ bool isConvex() const;
+
+ /**
+ * Checks whether the polygon is concave.
+ * @return Returns true if the polygon is concave. Returns false if the polygon is convex.
+ * @remark This method only returns a meaningful result if the polygon has at least three Vertecies.
+ */
+ bool isConcave() const;
+
+ /**
+ * Checks whether a point is inside the polygon
+ * @param Vertex A Vertex with the co-ordinates of the point to be tested.
+ * @param BorderBelongsToPolygon Specifies whether the edge of the polygon should be considered
+ * @return Returns true if the point is inside the polygon, false if it is outside.
+ */
+ bool isPointInPolygon(const Vertex &vertex, bool borderBelongsToPolygon = true) const;
+
+ /**
+ * Checks whether a point is inside the polygon
+ * @param X The X position of the point
+ * @param Y The Y position of the point
+ * @param BorderBelongsToPolygon Specifies whether the edge of the polygon should be considered
+ * @return Returns true if the point is inside the polygon, false if it is outside.
+ */
+ bool isPointInPolygon(int x, int y, bool borderBelongsToPolygon = true) const;
+
+ /**
+ * Returns the focus/centroid of the polygon
+ */
+ Vertex getCentroid() const;
+
+ // Edge belongs to the polygon
+ // Polygon must be CW
+ bool isLineInterior(const Vertex &a, const Vertex &b) const;
+ // Edge does not belong to the polygon
+ // Polygon must be CW
+ bool isLineExterior(const Vertex &a, const Vertex &b) const;
+
+ //
+ // Manipulation methods
+ //
+
+ /**
+ * Ensures that the Vertecies of the polygon are arranged in a clockwise direction
+ */
+ void ensureCWOrder();
+
+ /**
+ * Ensures that the Vertecies of the polygon are arranged in a counter-clockwise direction
+ */
+ void ensureCCWOrder();
+
+ /**
+ * Reverses the Vertecies order.
+ */
+ void reverseVertexOrder();
+
+ /**
+ * Moves the polygon.
+ * @param Delta The vertex around the polygon to be moved.
+ */
+ void operator+=(const Vertex &delta);
+
+ //
+ //------------------
+ //
+
+ /// Specifies the number of Vertecies in the Vertecies array.
+ int vertexCount;
+ /// COntains the Vertecies of the polygon
+ Vertex *vertices;
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool _isCW;
+ bool _isConvex;
+ Vertex _centroid;
+
+ /**
+ * Computes the centroid of the polygon.
+ */
+ Vertex computeCentroid() const;
+
+ /**
+ * Determines how the Vertecies of the polygon are arranged.
+ * @return Returns true if the Vertecies are arranged in a clockwise
+ * direction, otherwise false.
+ */
+ bool computeIsCW() const;
+
+ /**
+ * Determines whether the polygon is convex or concave.
+ * @return Returns true if the polygon is convex, otherwise false.
+ */
+ bool computeIsConvex() const;
+
+ /**
+ * Calculates the cross product of three Vertecies
+ * @param V1 The first Vertex
+ * @param V2 The second Vertex
+ * @param V3 The third Vertex
+ * @return Returns the cross-product of the three vertecies
+ * @todo This method would be better as a method of the BS_Vertex class
+ */
+ int crossProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const;
+
+ /**
+ * Computes the scalar product of two vectors spanning three vertecies
+ *
+ * The vectors are spanned by V2->V1 and V2->V3
+ *
+ * @param V1 The first Vertex
+ * @param V2 The second Vertex
+ * @param V3 The third Vertex
+ * @return Returns the dot product of the three Vertecies.
+ * @todo This method would be better as a method of the BS_Vertex class
+ */
+ int dotProduct(const Vertex &v1, const Vertex &v2, const Vertex &v3) const;
+
+ /**
+ * Checks whether the polygon is self-intersecting
+ * @return Returns true if the polygon is self-intersecting.
+ * Returns false if the polygon is not self-intersecting.
+ */
+ bool checkForSelfIntersection() const;
+
+ /**
+ * Find the vertex of the polygon that is located below the right-most point,
+ * and returns it's index in the vertex array.
+ * @return Returns the index of the vertex at the bottom-right of the polygon.
+ * Returns -1 if the vertex list is empty.
+ */
+ int findLRVertexIndex() const;
+
+ bool isLineInCone(int startVertexIndex, const Vertex &endVertex, bool includeEdges) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/region.cpp b/engines/sword25/math/region.cpp
new file mode 100644
index 0000000000..454f90a8ba
--- /dev/null
+++ b/engines/sword25/math/region.cpp
@@ -0,0 +1,354 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+
+#include "sword25/math/region.h"
+#include "sword25/math/walkregion.h"
+#include "sword25/math/regionregistry.h"
+
+#define BS_LOG_PREFIX "REGION"
+
+namespace Sword25 {
+
+Region::Region() : _valid(false), _type(RT_REGION) {
+ RegionRegistry::getInstance().registerObject(this);
+}
+
+Region::Region(InputPersistenceBlock &reader, uint handle) : _valid(false), _type(RT_REGION) {
+ RegionRegistry::getInstance().registerObject(this, handle);
+ unpersist(reader);
+}
+
+uint Region::create(REGION_TYPE type) {
+ Region *regionPtr = NULL;
+ switch (type) {
+ case RT_REGION:
+ regionPtr = new Region();
+ break;
+
+ case RT_WALKREGION:
+ regionPtr = new WalkRegion();
+ break;
+
+ default:
+ BS_ASSERT(true);
+ }
+
+ return RegionRegistry::getInstance().resolvePtr(regionPtr);
+}
+
+uint Region::create(InputPersistenceBlock &reader, uint handle) {
+ // Read type
+ uint type;
+ reader.read(type);
+
+ // Depending on the type, create a new BS_Region or BS_WalkRegion object
+ Region *regionPtr = NULL;
+ if (type == RT_REGION) {
+ regionPtr = new Region(reader, handle);
+ } else if (type == RT_WALKREGION) {
+ regionPtr = new WalkRegion(reader, handle);
+ } else {
+ BS_ASSERT(false);
+ }
+
+ return RegionRegistry::getInstance().resolvePtr(regionPtr);
+}
+
+Region::~Region() {
+ RegionRegistry::getInstance().deregisterObject(this);
+}
+
+bool Region::init(const Polygon &contour, const Common::Array<Polygon> *pHoles) {
+ // Reset object state
+ _valid = false;
+ _position = Vertex(0, 0);
+ _polygons.clear();
+
+ // Reserve sufficient space for countour and holes in the polygon list
+ if (pHoles)
+ _polygons.reserve(1 + pHoles->size());
+ else
+ _polygons.reserve(1);
+
+ // The first polygon will be the contour
+ _polygons.push_back(Polygon());
+ _polygons[0].init(contour.vertexCount, contour.vertices);
+ // Make sure that the Vertecies in the Contour are arranged in a clockwise direction
+ _polygons[0].ensureCWOrder();
+
+ // Place the hole polygons in the following positions
+ if (pHoles) {
+ for (uint i = 0; i < pHoles->size(); ++i) {
+ _polygons.push_back(Polygon());
+ _polygons[i + 1].init((*pHoles)[i].vertexCount, (*pHoles)[i].vertices);
+ _polygons[i + 1].ensureCWOrder();
+ }
+ }
+
+
+ // Initialise bounding box
+ updateBoundingBox();
+
+ _valid = true;
+ return true;
+}
+
+void Region::updateBoundingBox() {
+ if (_polygons[0].vertexCount) {
+ int minX = _polygons[0].vertices[0].x;
+ int maxX = _polygons[0].vertices[0].x;
+ int minY = _polygons[0].vertices[0].y;
+ int maxY = _polygons[0].vertices[0].y;
+
+ for (int i = 1; i < _polygons[0].vertexCount; i++) {
+ if (_polygons[0].vertices[i].x < minX) minX = _polygons[0].vertices[i].x;
+ else if (_polygons[0].vertices[i].x > maxX) maxX = _polygons[0].vertices[i].x;
+ if (_polygons[0].vertices[i].y < minY) minY = _polygons[0].vertices[i].y;
+ else if (_polygons[0].vertices[i].y > maxY) maxY = _polygons[0].vertices[i].y;
+ }
+
+ _boundingBox = Common::Rect(minX, minY, maxX + 1, maxY + 1);
+ }
+}
+
+// Position Changes
+void Region::setPos(int x, int y) {
+ // Calculate the difference between the old and new position
+ Vertex delta(x - _position.x, y - _position.y);
+
+ // Save the new position
+ _position = Vertex(x, y);
+
+ // Move all the vertecies
+ for (uint i = 0; i < _polygons.size(); ++i) {
+ _polygons[i] += delta;
+ }
+
+ // Update the bounding box
+ updateBoundingBox();
+}
+
+void Region::setPosX(int x) {
+ setPos(x, _position.y);
+}
+
+void Region::setPosY(int y) {
+ setPos(_position.x, y);
+}
+
+// Point-Region Tests
+bool Region::isPointInRegion(int x, int y) const {
+ // Test whether the point is in the bounding box
+ if (_boundingBox.contains(x, y)) {
+ // Test whether the point is in the contour
+ if (_polygons[0].isPointInPolygon(x, y, true)) {
+ // Test whether the point is in a hole
+ for (uint i = 1; i < _polygons.size(); i++) {
+ if (_polygons[i].isPointInPolygon(x, y, false))
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Region::isPointInRegion(const Vertex &vertex) const {
+ return isPointInRegion(vertex.x, vertex.y);
+}
+
+Vertex Region::findClosestRegionPoint(const Vertex &point) const {
+ // Determine whether the point is inside a hole. If that is the case, the closest
+ // point on the edge of the hole is determined
+ int polygonIdx = 0;
+ {
+ for (uint i = 1; i < _polygons.size(); ++i) {
+ if (_polygons[i].isPointInPolygon(point)) {
+ polygonIdx = i;
+ break;
+ }
+ }
+ }
+
+ const Polygon &polygon = _polygons[polygonIdx];
+
+ BS_ASSERT(polygon.vertexCount > 1);
+
+ // For each line of the polygon, calculate the point that is cloest to the given point
+ // The point of this set with the smallest distance to the given point is the result.
+ Vertex closestVertex = findClosestPointOnLine(polygon.vertices[0], polygon.vertices[1], point);
+ int closestVertexDistance2 = closestVertex.distance(point);
+ for (int i = 1; i < polygon.vertexCount; ++i) {
+ int j = (i + 1) % polygon.vertexCount;
+
+ Vertex curVertex = findClosestPointOnLine(polygon.vertices[i], polygon.vertices[j], point);
+ if (curVertex.distance(point) < closestVertexDistance2) {
+ closestVertex = curVertex;
+ closestVertexDistance2 = curVertex.distance(point);
+ }
+ }
+
+ // Determine whether the point is really within the region. This must not be so, as a result of rounding
+ // errors can occur at the edge of polygons
+ if (isPointInRegion(closestVertex))
+ return closestVertex;
+ else {
+ // Try to construct a point within the region - 8 points are tested in the immediate vacinity
+ // of the point
+ if (isPointInRegion(closestVertex + Vertex(-2, -2)))
+ return closestVertex + Vertex(-2, -2);
+ else if (isPointInRegion(closestVertex + Vertex(0, -2)))
+ return closestVertex + Vertex(0, -2);
+ else if (isPointInRegion(closestVertex + Vertex(2, -2)))
+ return closestVertex + Vertex(2, -2);
+ else if (isPointInRegion(closestVertex + Vertex(-2, 0)))
+ return closestVertex + Vertex(-2, 0);
+ else if (isPointInRegion(closestVertex + Vertex(0, 2)))
+ return closestVertex + Vertex(0, 2);
+ else if (isPointInRegion(closestVertex + Vertex(-2, 2)))
+ return closestVertex + Vertex(-2, 2);
+ else if (isPointInRegion(closestVertex + Vertex(-2, 0)))
+ return closestVertex + Vertex(2, 2);
+ else if (isPointInRegion(closestVertex + Vertex(2, 2)))
+ return closestVertex + Vertex(2, 2);
+
+ // If no point could be found that way that lies within the region, find the next point
+ closestVertex = polygon.vertices[0];
+ int shortestVertexDistance2 = polygon.vertices[0].distance2(point);
+ {
+ for (int i = 1; i < polygon.vertexCount; i++) {
+ int curDistance2 = polygon.vertices[i].distance2(point);
+ if (curDistance2 < shortestVertexDistance2) {
+ closestVertex = polygon.vertices[i];
+ shortestVertexDistance2 = curDistance2;
+ }
+ }
+ }
+
+ BS_LOG_WARNINGLN("Clostest vertex forced because edgepoint was outside region.");
+ return closestVertex;
+ }
+}
+
+Vertex Region::findClosestPointOnLine(const Vertex &lineStart, const Vertex &lineEnd, const Vertex point) const {
+ float vector1X = static_cast<float>(point.x - lineStart.x);
+ float vector1Y = static_cast<float>(point.y - lineStart.y);
+ float vector2X = static_cast<float>(lineEnd.x - lineStart.x);
+ float vector2Y = static_cast<float>(lineEnd.y - lineStart.y);
+ float vector2Length = sqrtf(vector2X * vector2X + vector2Y * vector2Y);
+ vector2X /= vector2Length;
+ vector2Y /= vector2Length;
+ float distance = sqrtf(static_cast<float>((lineStart.x - lineEnd.x) * (lineStart.x - lineEnd.x) +
+ (lineStart.y - lineEnd.y) * (lineStart.y - lineEnd.y)));
+ float dot = vector1X * vector2X + vector1Y * vector2Y;
+
+ if (dot <= 0)
+ return lineStart;
+ if (dot >= distance)
+ return lineEnd;
+
+ Vertex vector3(static_cast<int>(vector2X * dot + 0.5f), static_cast<int>(vector2Y * dot + 0.5f));
+ return lineStart + vector3;
+}
+
+// Line of Sight
+bool Region::isLineOfSight(const Vertex &a, const Vertex &b) const {
+ BS_ASSERT(_polygons.size());
+
+ // The line must be within the contour polygon, and outside of any hole polygons
+ Common::Array<Polygon>::const_iterator iter = _polygons.begin();
+ if (!(*iter).isLineInterior(a, b)) return false;
+ for (iter++; iter != _polygons.end(); iter++)
+ if (!(*iter).isLineExterior(a, b)) return false;
+
+ return true;
+}
+
+// Persistence
+bool Region::persist(OutputPersistenceBlock &writer) {
+ bool Result = true;
+
+ writer.write(static_cast<uint>(_type));
+ writer.write(_valid);
+ writer.write(_position.x);
+ writer.write(_position.y);
+
+ writer.write(_polygons.size());
+ Common::Array<Polygon>::iterator It = _polygons.begin();
+ while (It != _polygons.end()) {
+ Result &= It->persist(writer);
+ ++It;
+ }
+
+ writer.write(_boundingBox.left);
+ writer.write(_boundingBox.top);
+ writer.write(_boundingBox.right);
+ writer.write(_boundingBox.bottom);
+
+ return Result;
+}
+
+bool Region::unpersist(InputPersistenceBlock &reader) {
+ reader.read(_valid);
+ reader.read(_position.x);
+ reader.read(_position.y);
+
+ _polygons.clear();
+ uint PolygonCount;
+ reader.read(PolygonCount);
+ for (uint i = 0; i < PolygonCount; ++i) {
+ _polygons.push_back(Polygon(reader));
+ }
+
+ reader.read(_boundingBox.left);
+ reader.read(_boundingBox.top);
+ reader.read(_boundingBox.right);
+ reader.read(_boundingBox.bottom);
+
+ return reader.isGood();
+}
+
+Vertex Region::getCentroid() const {
+ if (_polygons.size() > 0)
+ return _polygons[0].getCentroid();
+ return
+ Vertex();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/region.h b/engines/sword25/math/region.h
new file mode 100644
index 0000000000..fac9f98bb6
--- /dev/null
+++ b/engines/sword25/math/region.h
@@ -0,0 +1,241 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_REGION_H
+#define SWORD25_REGION_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/math/vertex.h"
+#include "sword25/math/polygon.h"
+#include "common/rect.h"
+
+namespace Sword25 {
+
+/**
+ * This class is the base class of all regions.
+ *
+ * The IsValid() method can be queried to see whether the object is in a valid state.
+ * If this is not the case, the method Init() is the only method that may be invoked.
+ * This class guarantees that the Vertecies outline of the hole, and the polygons are
+ * arranged in a clockwise direction, so that the polygon working algorithms will
+ * work properly.
+ */
+class Region : public Persistable {
+protected:
+ /**
+ * Creates a new BS_Region object
+ *
+ * After creation the object is invaild (IsValid() return false), but a call can
+ * be made later on to Init() to set up the region into a valid state.
+ */
+ Region();
+
+ Region(InputPersistenceBlock &reader, uint handle);
+
+public:
+ enum REGION_TYPE {
+ RT_REGION,
+ RT_WALKREGION
+ };
+
+ static uint create(REGION_TYPE type);
+ static uint create(InputPersistenceBlock &reader, uint handle = 0);
+
+ virtual ~Region();
+
+ /**
+ * Initialises a BS_Region object
+ * @param Contour A polygon indicating the outline of the region
+ * @param pHoles A pointer to an array of polygons representing the hole state in the region.
+ * If the region has no holes, it must be passed as NULL. The default value is NULL.
+ * @return Returns true if the initialisation was successful, otherwise false.
+ * @remark If the region was already initialised, the old state will be deleted.
+ */
+ virtual bool init(const Polygon &contour, const Common::Array<Polygon> *pHoles = NULL);
+
+ //
+ // Exploratory Methods
+ //
+
+ /**
+ * Specifies whether the object is in a valid state
+ * @return Returns true if the object is in a valid state, otherwise false.
+ * @remark Invalid objects can be made valid by calling Init with a valid state.
+ */
+ bool isValid() const {
+ return _valid;
+ }
+
+ /**
+ * Returns the position of the region
+ */
+ const Vertex &getPosition() const {
+ return _position;
+ }
+
+ /**
+ * Returns the X position of the region
+ */
+ int getPosX() const {
+ return _position.x;
+ }
+
+ /**
+ * Returns the Y position of the region
+ */
+ int getPosY() const {
+ return _position.y;
+ }
+
+ /**
+ * Indicates whether a point is inside the region
+ * @param Vertex A verex with the co-ordinates of the test point
+ * @return Returns true if the point is within the region, otherwise false.
+ */
+ bool isPointInRegion(const Vertex &vertex) const;
+
+ /**
+ * Indicates whether a point is inside the region
+ * @param X The X position
+ * @param Y The Y position
+ * @return Returns true if the point is within the region, otherwise false.
+ */
+ bool isPointInRegion(int x, int y) const;
+
+ /**
+ * Returns the countour of the region
+ */
+ const Polygon &getContour() const {
+ return _polygons[0];
+ }
+
+ /**
+ * Returns the number of polygons in the hole region
+ */
+ int getHoleCount() const {
+ return static_cast<int>(_polygons.size() - 1);
+ }
+
+ /**
+ * Returns a specific hole polygon in the region
+ * @param i The number of the hole to return.
+ * The index must be between 0 and GetHoleCount() - 1.
+ * @return Returns the desired hole polygon
+ */
+ inline const Polygon &getHole(uint i) const;
+
+ /**
+ * For a point outside the region, finds the closest point inside the region
+ * @param Point The point that is outside the region
+ * @return Returns the point within the region which is closest
+ * @remark This method does not always work with pixel accuracy.
+ * One should not therefore rely on the fact that there is really no point in
+ * the region which is closer to the given point.
+ */
+ Vertex findClosestRegionPoint(const Vertex &point) const;
+
+ /**
+ * Returns the centroid for the region
+ */
+ Vertex getCentroid() const;
+
+ bool isLineOfSight(const Vertex &a, const Vertex &b) const;
+
+ //
+ // Manipulation Methods
+ //
+
+ /**
+ * Sets the position of the region
+ * @param X The new X psoition of the region
+ * @param Y The new Y psoition of the region
+ */
+ virtual void setPos(int x, int y);
+
+ /**
+ * Sets the X position of the region
+ * @param X The new X position of the region
+ */
+ void setPosX(int x);
+
+ /**
+ * Sets the Y position of the region
+ * @param Y The new Y position of the region
+ */
+ void setPosY(int y);
+
+ //
+ // Manipulation Methods
+ //
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+protected:
+ /// This specifies the type of object
+ REGION_TYPE _type;
+ /// This variable indicates whether the current object state is valid
+ bool _valid;
+ /// This vertex is the position of the region
+ Vertex _position;
+ /// This array contains all the polygons that define the region. The first element of
+ // the array is the contour, all others are the holes
+ Common::Array<Polygon> _polygons;
+ /// The bounding box for the region
+ Common::Rect _boundingBox;
+
+ /**
+ * Updates the bounding box of the region.
+ */
+ void updateBoundingBox();
+
+ /**
+ * Find the point on a line which is closest to another point
+ * @param LineStart The start of the line
+ * @param LineEnd The end of the line
+ * @param Point The point to be compared against
+ * @return Returns the point on the line which is cloest to the passed point.
+ */
+ Vertex findClosestPointOnLine(const Vertex &lineStart, const Vertex &lineEnd, const Vertex point) const;
+};
+
+inline const Polygon &Region::getHole(uint i) const {
+ BS_ASSERT(i < _polygons.size() - 1);
+ return _polygons[i + 1];
+}
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/regionregistry.cpp b/engines/sword25/math/regionregistry.cpp
new file mode 100644
index 0000000000..1509ea9e5e
--- /dev/null
+++ b/engines/sword25/math/regionregistry.cpp
@@ -0,0 +1,106 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "REGIONREGISTRY"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/math/regionregistry.h"
+#include "sword25/math/region.h"
+
+namespace Sword25 {
+
+Common::SharedPtr<RegionRegistry> RegionRegistry::_instancePtr;
+
+void RegionRegistry::logErrorLn(const char *message) const {
+ BS_LOG_ERRORLN(message);
+}
+
+void RegionRegistry::logWarningLn(const char *message) const {
+ BS_LOG_WARNINGLN(message);
+}
+
+bool RegionRegistry::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // write out the next handle
+ writer.write(_nextHandle);
+
+ // Number of regions to write
+ writer.write(_handle2PtrMap.size());
+
+ // Persist all the BS_Regions
+ HANDLE2PTR_MAP::const_iterator iter = _handle2PtrMap.begin();
+ while (iter != _handle2PtrMap.end()) {
+ // Handle persistence
+ writer.write(iter->_key);
+
+ // Persist object
+ result &= iter->_value->persist(writer);
+
+ ++iter;
+ }
+
+ return result;
+}
+
+bool RegionRegistry::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // read in the next handle
+ reader.read(_nextHandle);
+
+ // Destroy all existing BS_Regions
+//FIXME: This doesn't seem right - the value is being deleted but not the actual hash node itself?
+ while (!_handle2PtrMap.empty())
+ delete _handle2PtrMap.begin()->_value;
+
+ // read in the number of BS_Regions
+ uint regionCount;
+ reader.read(regionCount);
+
+ // Restore all the BS_Regions objects
+ for (uint i = 0; i < regionCount; ++i) {
+ // Handle read
+ uint handle;
+ reader.read(handle);
+
+ // BS_Region restore
+ result &= Region::create(reader, handle) != 0;
+ }
+
+ return reader.isGood() && result;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/regionregistry.h b/engines/sword25/math/regionregistry.h
new file mode 100644
index 0000000000..bbe2fb8370
--- /dev/null
+++ b/engines/sword25/math/regionregistry.h
@@ -0,0 +1,66 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_REGIONREGISTRY_H
+#define SWORD25_REGIONREGISTRY_H
+
+#include "common/ptr.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/persistable.h"
+#include "sword25/kernel/objectregistry.h"
+
+namespace Sword25 {
+
+class Region;
+
+class RegionRegistry : public ObjectRegistry<Region>, public Persistable {
+public:
+ static RegionRegistry &getInstance() {
+ if (!_instancePtr.get()) _instancePtr = Common::SharedPtr<RegionRegistry>(new RegionRegistry());
+ return *_instancePtr.get();
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ virtual void logErrorLn(const char *message) const;
+ virtual void logWarningLn(const char *message) const;
+
+ static Common::SharedPtr<RegionRegistry> _instancePtr;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/vertex.cpp b/engines/sword25/math/vertex.cpp
new file mode 100644
index 0000000000..4997da09d3
--- /dev/null
+++ b/engines/sword25/math/vertex.cpp
@@ -0,0 +1,93 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/math/vertex.h"
+
+namespace Lua {
+
+extern "C"
+{
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+}
+
+}
+
+namespace Sword25 {
+
+Vertex &Vertex::luaVertexToVertex(lua_State *L, int stackIndex, Vertex &vertex) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Ensure that we actually consider a table
+ luaL_checktype(L, stackIndex, LUA_TTABLE);
+
+ // Read X Component
+ lua_pushstring(L, "X");
+ lua_gettable(L, stackIndex);
+ if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, stackIndex, "the X component has to be a number");
+ vertex.x = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+ // Read Y Component
+ lua_pushstring(L, "Y");
+ lua_gettable(L, stackIndex);
+ if (!lua_isnumber(L, -1)) luaL_argcheck(L, 0, stackIndex, "the Y component has to be a number");
+ vertex.y = static_cast<int>(lua_tonumber(L, -1));
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return vertex;
+}
+
+void Vertex::vertexToLuaVertex(lua_State *L, const Vertex &vertex) {
+ // Create New Table
+ lua_newtable(L);
+
+ // X value is written to table
+ lua_pushstring(L, "X");
+ lua_pushnumber(L, vertex.x);
+ lua_settable(L, -3);
+
+ // Y value is written to table
+ lua_pushstring(L, "Y");
+ lua_pushnumber(L, vertex.y);
+ lua_settable(L, -3);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/vertex.h b/engines/sword25/math/vertex.h
new file mode 100644
index 0000000000..87e4694d48
--- /dev/null
+++ b/engines/sword25/math/vertex.h
@@ -0,0 +1,180 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ BS_Vertex
+ ---------
+
+ Autor: Malte Thiesen
+*/
+
+#ifndef SWORD25_VERTEX_H
+#define SWORD25_VERTEX_H
+
+// Includes
+#include <math.h>
+#include "sword25/kernel/common.h"
+
+namespace Lua {
+
+// Forward declarations
+struct lua_State;
+
+}
+
+using namespace Lua;
+
+namespace Sword25 {
+
+/**
+ * Defines a 2-D Vertex
+ */
+class Vertex {
+public:
+ Vertex() : x(0), y(0) {};
+ Vertex(int x_, int y_) {
+ this->x = x_;
+ this->y = y_;
+ }
+
+ int x;
+ int y;
+
+ /**
+ * Compares two Vertecies.
+ */
+ inline bool operator==(const Vertex &rhs) const {
+ if (x == rhs.x && y == rhs.y) return true;
+ return false;
+ }
+ /**
+ * Compares two Vertecies.
+ */
+ inline bool operator!=(const Vertex &rhs) const {
+ if (x != rhs.x || y != rhs.y) return true;
+ return false;
+ }
+ /**
+ * Adds a vertex to vertex
+ */
+ inline void operator+=(const Vertex &delta) {
+ x += delta.x;
+ y += delta.y;
+ }
+
+ /**
+ * Subtracts a vertex from a vertex
+ */
+ inline void operator-=(const Vertex &delta) {
+ x -= delta.x;
+ y -= delta.y;
+ }
+
+ /**
+ * Adds two vertecies
+ */
+ inline Vertex operator+(const Vertex &delta) const {
+ return Vertex(x + delta.x, y + delta.y);
+ }
+
+ /**
+ * Subtracts two vertecies
+ */
+ inline Vertex operator-(const Vertex &delta) const {
+ return Vertex(x - delta.x, y - delta.y);
+ }
+
+ /**
+ * Calculates the square of the distance between two Vertecies.
+ * @param Vertex The vertex for which the distance is to be calculated
+ * @return Returns the square of the distance between itself and the passed vertex
+ * @remark If only distances should be compared, this method should be used because
+ * it is faster than Distance()
+ */
+ inline int distance2(const Vertex &vertex) const {
+ return (x - vertex.x) * (x - vertex.x) + (y - vertex.y) * (y - vertex.y);
+ }
+
+ /**
+ * Calculates the square of the distance between two Vertecies.
+ * @param Vertex The vertex for which the distance is to be calculated
+ * @return Returns the square of the distance between itself and the passed vertex
+ * @remark If only distances should be compared, Distance2() should be used, since it is faster.
+ */
+ inline int distance(const Vertex &vertex) const {
+ return (int)(sqrtf(static_cast<float>(distance2(vertex))) + 0.5);
+ }
+
+ /**
+ * Calculates the cross product of the vertex with another vertex. Here the Vertecies will be
+ * interpreted as vectors.
+ * @param Vertex The second vertex
+ * @return Returns the cross product of this vertex and the passed vertex.
+ */
+ inline int computeCrossProduct(const Vertex &vertex) const {
+ return x * vertex.y - vertex.x * y;
+ }
+
+ /**
+ * Returns the dot product of this vertex with another vertex. Here the Vertecies are interpreted as vectors.
+ * @param Vertex The second vertex
+ * @return Returns the dot product of this vertex and the passed vertex.
+ */
+ inline int computeDotProduct(const Vertex &vertex) const {
+ return x * vertex.x + y * vertex.y;
+ }
+
+ /**
+ * Calculates the angle between this vertex and another vertex. Here the Vertecies are interpreted as vectors.
+ * @param Vertex The second vertex
+ * @return Returns the angle between this vertex and the passed vertex in radians.
+ */
+ inline float computeAngle(const Vertex &vertex) const {
+ return atan2f(static_cast<float>(computeCrossProduct(vertex)), static_cast<float>(computeDotProduct(vertex)));
+ }
+
+ /**
+ * Calculates the length of the vector
+ */
+ inline float computeLength() const {
+ return sqrtf(static_cast<float>(x * x + y * y));
+ }
+
+ static Vertex &luaVertexToVertex(lua_State *L, int StackIndex, Vertex &vertex);
+ static void vertexToLuaVertex(lua_State *L, const Vertex &vertex);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/math/walkregion.cpp b/engines/sword25/math/walkregion.cpp
new file mode 100644
index 0000000000..7cdd8c64c5
--- /dev/null
+++ b/engines/sword25/math/walkregion.cpp
@@ -0,0 +1,390 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/math/walkregion.h"
+#include "sword25/math/line.h"
+
+#define BS_LOG_PREFIX "WALKREGION"
+
+namespace Sword25 {
+
+static const int Infinity = 0x7fffffff;
+
+WalkRegion::WalkRegion() {
+ _type = RT_WALKREGION;
+}
+
+WalkRegion::WalkRegion(InputPersistenceBlock &reader, uint handle) :
+ Region(reader, handle) {
+ _type = RT_WALKREGION;
+ unpersist(reader);
+}
+
+WalkRegion::~WalkRegion() {
+}
+
+bool WalkRegion::init(const Polygon &contour, const Common::Array<Polygon> *pHoles) {
+ // Default initialisation of the region
+ if (!Region::init(contour, pHoles)) return false;
+
+ // Prepare structures for pathfinding
+ initNodeVector();
+ computeVisibilityMatrix();
+
+ // Signal success
+ return true;
+}
+
+bool WalkRegion::queryPath(Vertex startPoint, Vertex endPoint, BS_Path &path) {
+ BS_ASSERT(path.empty());
+
+ // If the start and finish are identical, no path can be found trivially
+ if (startPoint == endPoint)
+ return true;
+
+ // Ensure that the start and finish are valid and find new start points if either
+ // are outside the polygon
+ if (!checkAndPrepareStartAndEnd(startPoint, endPoint)) return false;
+
+ // If between the start and point a line of sight exists, then it can be returned.
+ if (isLineOfSight(startPoint, endPoint)) {
+ path.push_back(startPoint);
+ path.push_back(endPoint);
+ return true;
+ }
+
+ return findPath(startPoint, endPoint, path);
+}
+
+struct DijkstraNode {
+ typedef Common::Array<DijkstraNode> Container;
+ typedef Container::iterator Iter;
+ typedef Container::const_iterator ConstIter;
+
+ DijkstraNode() : cost(Infinity), chosen(false) {};
+ ConstIter parentIter;
+ int cost;
+ bool chosen;
+};
+
+static void initDijkstraNodes(DijkstraNode::Container &dijkstraNodes, const Region &region,
+ const Vertex &start, const Common::Array<Vertex> &nodes) {
+ // Allocate sufficient space in the array
+ dijkstraNodes.resize(nodes.size());
+
+ // Initialise all the nodes which are visible from the starting node
+ DijkstraNode::Iter dijkstraIter = dijkstraNodes.begin();
+ for (Common::Array<Vertex>::const_iterator nodesIter = nodes.begin();
+ nodesIter != nodes.end(); nodesIter++, dijkstraIter++) {
+ (*dijkstraIter).parentIter = dijkstraNodes.end();
+ if (region.isLineOfSight(*nodesIter, start))(*dijkstraIter).cost = (*nodesIter).distance(start);
+ }
+ BS_ASSERT(dijkstraIter == dijkstraNodes.end());
+}
+
+static DijkstraNode::Iter chooseClosestNode(DijkstraNode::Container &nodes) {
+ DijkstraNode::Iter closestNodeInter = nodes.end();
+ int minCost = Infinity;
+
+ for (DijkstraNode::Iter iter = nodes.begin(); iter != nodes.end(); iter++) {
+ if (!(*iter).chosen && (*iter).cost < minCost) {
+ minCost = (*iter).cost;
+ closestNodeInter = iter;
+ }
+ }
+
+ return closestNodeInter;
+}
+
+static void relaxNodes(DijkstraNode::Container &nodes,
+ const Common::Array< Common::Array<int> > &visibilityMatrix,
+ const DijkstraNode::ConstIter &curNodeIter) {
+ // All the successors of the current node that have not been chosen will be
+ // inserted into the boundary node list, and the cost will be updated if
+ // a shorter path has been found to them.
+
+ int curNodeIndex = curNodeIter - nodes.begin();
+ for (uint i = 0; i < nodes.size(); i++) {
+ int cost = visibilityMatrix[curNodeIndex][i];
+ if (!nodes[i].chosen && cost != Infinity) {
+ int totalCost = (*curNodeIter).cost + cost;
+ if (totalCost < nodes[i].cost) {
+ nodes[i].parentIter = curNodeIter;
+ nodes[i].cost = totalCost;
+ }
+ }
+ }
+}
+
+static void relaxEndPoint(const Vertex &curNodePos,
+ const DijkstraNode::ConstIter &curNodeIter,
+ const Vertex &endPointPos,
+ DijkstraNode &endPoint,
+ const Region &region) {
+ if (region.isLineOfSight(curNodePos, endPointPos)) {
+ int totalCost = (*curNodeIter).cost + curNodePos.distance(endPointPos);
+ if (totalCost < endPoint.cost) {
+ endPoint.parentIter = curNodeIter;
+ endPoint.cost = totalCost;
+ }
+ }
+}
+
+bool WalkRegion::findPath(const Vertex &start, const Vertex &end, BS_Path &path) const {
+ // This is an implementation of Dijkstra's algorithm
+
+ // Initialise edge node list
+ DijkstraNode::Container dijkstraNodes;
+ initDijkstraNodes(dijkstraNodes, *this, start, _nodes);
+
+ // The end point is treated separately, since it does not exist in the visibility graph
+ DijkstraNode endPoint;
+
+ // Since a node is selected each round from the node list, and can never be selected again
+ // after that, the maximum number of loop iterations is limited by the number of nodes
+ for (uint i = 0; i < _nodes.size(); i++) {
+ // Determine the nearest edge node in the node list
+ DijkstraNode::Iter nodeInter = chooseClosestNode(dijkstraNodes);
+
+ // If no free nodes are absent from the edge node list, there is no path from start
+ // to end node. This case should never occur, since the number of loop passes is
+ // limited, but etter safe than sorry
+ if (nodeInter == dijkstraNodes.end())
+ return false;
+
+ // If the destination point is closer than the point cost, scan can stop
+ (*nodeInter).chosen = true;
+ if (endPoint.cost <= (*nodeInter).cost) {
+ // Insert the end point in the list
+ path.push_back(end);
+
+ // The list is done in reverse order and inserted into the path
+ DijkstraNode::ConstIter curNode = endPoint.parentIter;
+ while (curNode != dijkstraNodes.end()) {
+ BS_ASSERT((*curNode).chosen);
+ path.push_back(_nodes[curNode - dijkstraNodes.begin()]);
+ curNode = (*curNode).parentIter;
+ }
+
+ // The starting point is inserted into the path
+ path.push_back(start);
+
+ // The nodes of the path must be untwisted, as they were extracted in reverse order.
+ // This step could be saved if the path from end to the beginning was desired
+ ReverseArray<Vertex>(path);
+
+ return true;
+ }
+
+ // Relaxation step for nodes of the graph, and perform the end nodes
+ relaxNodes(dijkstraNodes, _visibilityMatrix, nodeInter);
+ relaxEndPoint(_nodes[nodeInter - dijkstraNodes.begin()], nodeInter, end, endPoint, *this);
+ }
+
+ // If the loop has been completely run through, all the nodes have been chosen, and still
+ // no path was found. There is therefore no path available
+ return false;
+}
+
+void WalkRegion::initNodeVector() {
+ // Empty the Node list
+ _nodes.clear();
+
+ // Determine the number of nodes
+ int nodeCount = 0;
+ {
+ for (uint i = 0; i < _polygons.size(); i++)
+ nodeCount += _polygons[i].vertexCount;
+ }
+
+ // Knoten-Vector füllen
+ _nodes.reserve(nodeCount);
+ {
+ for (uint j = 0; j < _polygons.size(); j++)
+ for (int i = 0; i < _polygons[j].vertexCount; i++)
+ _nodes.push_back(_polygons[j].vertices[i]);
+ }
+}
+
+void WalkRegion::computeVisibilityMatrix() {
+ // Initialise visibility matrix
+ _visibilityMatrix = Common::Array< Common::Array <int> >();
+ for (uint idx = 0; idx < _nodes.size(); ++idx) {
+ Common::Array<int> arr;
+ for (uint idx2 = 0; idx2 < _nodes.size(); ++idx2)
+ arr.push_back(Infinity);
+
+ _visibilityMatrix.push_back(arr);
+ }
+
+ // Calculate visibility been vertecies
+ for (uint j = 0; j < _nodes.size(); ++j) {
+ for (uint i = j; i < _nodes.size(); ++i) {
+ if (isLineOfSight(_nodes[i], _nodes[j])) {
+ // There is a line of sight, so save the distance between the two
+ int distance = _nodes[i].distance(_nodes[j]);
+ _visibilityMatrix[i][j] = distance;
+ _visibilityMatrix[j][i] = distance;
+ } else {
+ // There is no line of sight, so save Infinity as the distance
+ _visibilityMatrix[i][j] = Infinity;
+ _visibilityMatrix[j][i] = Infinity;
+ }
+ }
+ }
+}
+
+bool WalkRegion::checkAndPrepareStartAndEnd(Vertex &start, Vertex &end) const {
+ if (!isPointInRegion(start)) {
+ Vertex newStart = findClosestRegionPoint(start);
+
+ // Check to make sure the point is really in the region. If not, stop with an error
+ if (!isPointInRegion(newStart)) {
+ BS_LOG_ERRORLN("Constructed startpoint ((%d,%d) from (%d,%d)) is not inside the region.",
+ newStart.x, newStart.y,
+ start.x, start.y);
+ return false;
+ }
+
+ start = newStart;
+ }
+
+ // If the destination is outside the region, a point is determined that is within the region,
+ // and that is used as an endpoint instead
+ if (!isPointInRegion(end)) {
+ Vertex newEnd = findClosestRegionPoint(end);
+
+ // Make sure that the determined point is really within the region
+ if (!isPointInRegion(newEnd)) {
+ BS_LOG_ERRORLN("Constructed endpoint ((%d,%d) from (%d,%d)) is not inside the region.",
+ newEnd.x, newEnd.y,
+ end.x, end.y);
+ return false;
+ }
+
+ end = newEnd;
+ }
+
+ // Signal success
+ return true;
+}
+
+void WalkRegion::setPos(int x, int y) {
+ // Calculate the difference between old and new position
+ Vertex Delta(x - _position.x, y - _position.y);
+
+ // Move all the nodes
+ for (uint i = 0; i < _nodes.size(); i++)
+ _nodes[i] += Delta;
+
+ // Move regions
+ Region::setPos(x, y);
+}
+
+bool WalkRegion::persist(OutputPersistenceBlock &writer) {
+ bool result = true;
+
+ // Persist the parent region
+ result &= Region::persist(writer);
+
+ // Persist the nodes
+ writer.write(_nodes.size());
+ Common::Array<Vertex>::const_iterator it = _nodes.begin();
+ while (it != _nodes.end()) {
+ writer.write(it->x);
+ writer.write(it->y);
+ ++it;
+ }
+
+ // Persist the visibility matrix
+ writer.write(_visibilityMatrix.size());
+ Common::Array< Common::Array<int> >::const_iterator rowIter = _visibilityMatrix.begin();
+ while (rowIter != _visibilityMatrix.end()) {
+ writer.write(rowIter->size());
+ Common::Array<int>::const_iterator colIter = rowIter->begin();
+ while (colIter != rowIter->end()) {
+ writer.write(*colIter);
+ ++colIter;
+ }
+
+ ++rowIter;
+ }
+
+ return result;
+}
+
+bool WalkRegion::unpersist(InputPersistenceBlock &reader) {
+ bool result = true;
+
+ // The parent object was already loaded in the constructor of BS_Region, so at
+ // this point only the additional data from BS_WalkRegion needs to be loaded
+
+ // Node load
+ uint nodeCount;
+ reader.read(nodeCount);
+ _nodes.clear();
+ _nodes.resize(nodeCount);
+ Common::Array<Vertex>::iterator it = _nodes.begin();
+ while (it != _nodes.end()) {
+ reader.read(it->x);
+ reader.read(it->y);
+ ++it;
+ }
+
+ // Visibility matrix load
+ uint rowCount;
+ reader.read(rowCount);
+ _visibilityMatrix.clear();
+ _visibilityMatrix.resize(rowCount);
+ Common::Array< Common::Array<int> >::iterator rowIter = _visibilityMatrix.begin();
+ while (rowIter != _visibilityMatrix.end()) {
+ uint colCount;
+ reader.read(colCount);
+ rowIter->resize(colCount);
+ Common::Array<int>::iterator colIter = rowIter->begin();
+ while (colIter != rowIter->end()) {
+ reader.read(*colIter);
+ ++colIter;
+ }
+
+ ++rowIter;
+ }
+
+ return result && reader.isGood();
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/math/walkregion.h b/engines/sword25/math/walkregion.h
new file mode 100644
index 0000000000..e8bf40becc
--- /dev/null
+++ b/engines/sword25/math/walkregion.h
@@ -0,0 +1,113 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_WALKREGION_H
+#define SWORD25_WALKREGION_H
+
+#include "common/array.h"
+#include "sword25/kernel/common.h"
+#include "sword25/math/region.h"
+
+namespace Sword25 {
+
+typedef Common::Array<Vertex> BS_Path;
+
+/**
+ * This class represents the region in which the main character can move
+ */
+class WalkRegion : public Region {
+ friend class Region;
+
+protected:
+ WalkRegion();
+ WalkRegion(InputPersistenceBlock &Reader, uint handle);
+
+public:
+ virtual ~WalkRegion();
+
+ virtual bool init(const Polygon &contour, const Common::Array<Polygon> *pHoles = 0);
+
+ /**
+ * Get the shortest path between two points in the region
+ *
+ * This method requires that the starting point lies within the region. The end point
+ * may lie outside the region. Int his case, the end is chosen as the cloest point to it
+ * that lies within the region.
+ *
+ * @param X1 X Co-ordinate of the start point
+ * @param Y1 Y Co-ordinate of the start point
+ * @param X2 X Co-ordinate of the end point
+ * @param Y2 Y Co-ordinate of the end point
+ * @param Path An empty BS_Path that will be set to the resulting path
+ * @return Returns false if the result is invalid, otherwise returns true.
+ */
+ bool queryPath(int x1, int y1, int x2, int y2, BS_Path &path) {
+ return queryPath(Vertex(x1, y1), Vertex(x2, y2), path);
+ }
+
+ /**
+ * Get the shortest path between two points in the region.
+ *
+ * @param StartPoint The start point
+ * @param EndPoint The end point
+ * @param Path An empty BS_Path that will be set to the resulting path
+ * @return Returns false if the result is invalid, otherwise returns true.
+ */
+ bool queryPath(Vertex startPoint, Vertex endPoint, BS_Path &path);
+
+ virtual void setPos(int x, int y);
+
+ const Common::Array<Vertex> &getNodes() const {
+ return _nodes;
+ }
+ const Common::Array< Common::Array<int> > &getVisibilityMatrix() const {
+ return _visibilityMatrix;
+ }
+
+ virtual bool persist(OutputPersistenceBlock &writer);
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ Common::Array<Vertex> _nodes;
+ Common::Array< Common::Array<int> > _visibilityMatrix;
+
+ void initNodeVector();
+ void computeVisibilityMatrix();
+ bool checkAndPrepareStartAndEnd(Vertex &start, Vertex &end) const;
+ bool findPath(const Vertex &start, const Vertex &end, BS_Path &path) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk
new file mode 100644
index 0000000000..ebe50976af
--- /dev/null
+++ b/engines/sword25/module.mk
@@ -0,0 +1,109 @@
+MODULE := engines/sword25
+
+MODULE_OBJS := \
+ detection.o \
+ sword25.o \
+ fmv/movieplayer.o \
+ fmv/movieplayer_script.o \
+ fmv/theora_decoder.o \
+ fmv/yuvtorgba.o \
+ gfx/animation.o \
+ gfx/animationdescription.o \
+ gfx/animationresource.o \
+ gfx/animationtemplate.o \
+ gfx/animationtemplateregistry.o \
+ gfx/bitmap.o \
+ gfx/bitmapresource.o \
+ gfx/dynamicbitmap.o \
+ gfx/fontresource.o \
+ gfx/framecounter.o \
+ gfx/graphicengine.o \
+ gfx/graphicengine_script.o \
+ gfx/panel.o \
+ gfx/renderobject.o \
+ gfx/renderobjectmanager.o \
+ gfx/renderobjectregistry.o \
+ gfx/screenshot.o \
+ gfx/staticbitmap.o \
+ gfx/text.o \
+ gfx/timedrenderobject.o \
+ gfx/image/art.o \
+ gfx/image/b25sloader.o \
+ gfx/image/imageloader.o \
+ gfx/image/pngloader.o \
+ gfx/image/renderedimage.o \
+ gfx/image/swimage.o \
+ gfx/image/vectorimage.o \
+ gfx/image/vectorimagerenderer.o \
+ input/inputengine.o \
+ input/inputengine_script.o \
+ kernel/callbackregistry.o \
+ kernel/filesystemutil.o \
+ kernel/inputpersistenceblock.o \
+ kernel/kernel.o \
+ kernel/kernel_script.o \
+ kernel/log.o \
+ kernel/outputpersistenceblock.o \
+ kernel/persistenceservice.o \
+ kernel/resmanager.o \
+ kernel/resource.o \
+ kernel/scummvmwindow.o \
+ kernel/window.o \
+ math/geometry.o \
+ math/geometry_script.o \
+ math/polygon.o \
+ math/region.o \
+ math/regionregistry.o \
+ math/vertex.o \
+ math/walkregion.o \
+ package/packagemanager.o \
+ package/packagemanager_script.o \
+ script/luabindhelper.o \
+ script/luacallback.o \
+ script/luascript.o \
+ script/lua_extensions.o \
+ sfx/soundengine.o \
+ sfx/soundengine_script.o \
+ util/lua/lapi.o \
+ util/lua/lauxlib.o \
+ util/lua/lbaselib.o \
+ util/lua/lcode.o \
+ util/lua/ldblib.o \
+ util/lua/ldebug.o \
+ util/lua/ldo.o \
+ util/lua/ldump.o \
+ util/lua/lfunc.o \
+ util/lua/lgc.o \
+ util/lua/linit.o \
+ util/lua/liolib.o \
+ util/lua/llex.o \
+ util/lua/lmathlib.o \
+ util/lua/lmem.o \
+ util/lua/loadlib.o \
+ util/lua/lobject.o \
+ util/lua/lopcodes.o \
+ util/lua/loslib.o \
+ util/lua/lparser.o \
+ util/lua/lstate.o \
+ util/lua/lstring.o \
+ util/lua/lstrlib.o \
+ util/lua/ltable.o \
+ util/lua/ltablib.o \
+ util/lua/ltm.o \
+ util/lua/lua.o \
+ util/lua/luac.o \
+ util/lua/lundump.o \
+ util/lua/lvm.o \
+ util/lua/lzio.o \
+ util/lua/print.o \
+ util/pluto/pdep.o \
+ util/pluto/pluto.o \
+ util/pluto/plzio.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp
new file mode 100644
index 0000000000..063844f3ba
--- /dev/null
+++ b/engines/sword25/package/packagemanager.cpp
@@ -0,0 +1,293 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "PACKAGEMANAGER"
+
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/system.h"
+#include "common/unzip.h"
+#include "sword25/kernel/filesystemutil.h"
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+const char PATH_SEPARATOR = '/';
+
+static Common::String normalizePath(const Common::String &path, const Common::String &currentDirectory) {
+ Common::String wholePath = (path.size() >= 1 && path[0] == PATH_SEPARATOR) ? path : currentDirectory + PATH_SEPARATOR + path;
+
+ if (wholePath.size() == 0) {
+ // The path list has no elements, therefore the root directory is returned
+ return Common::String(PATH_SEPARATOR);
+ }
+
+ return Common::normalizePath(wholePath, PATH_SEPARATOR);
+}
+
+PackageManager::PackageManager(Kernel *pKernel) : Service(pKernel),
+ _currentDirectory(PATH_SEPARATOR),
+ _rootFolder(ConfMan.get("path")) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+}
+
+PackageManager::~PackageManager() {
+ // Free the package list
+ Common::List<ArchiveEntry *>::iterator i;
+ for (i = _archiveList.begin(); i != _archiveList.end(); ++i)
+ delete *i;
+
+}
+
+Service *PackageManager_CreateObject(Kernel *kernelPtr) {
+ return new PackageManager(kernelPtr);
+}
+
+/**
+ * Scans through the archive list for a specified file
+ */
+Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) {
+ // Loop through checking each archive
+ Common::List<ArchiveEntry *>::iterator i;
+ for (i = _archiveList.begin(); i != _archiveList.end(); ++i) {
+ if (!fileName.hasPrefix((*i)->_mountPath)) {
+ // The mount path is in different subtree. Skipping
+ continue;
+ }
+
+ // Look into the archive for the desired file
+ Common::Archive *archiveFolder = (*i)->archive;
+
+ // Construct relative path
+ Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]);
+
+ if (archiveFolder->hasFile(resPath)) {
+ return archiveFolder->getMember(resPath);
+ }
+ }
+
+ return Common::ArchiveMemberPtr();
+}
+
+bool PackageManager::loadPackage(const Common::String &fileName, const Common::String &mountPosition) {
+ debug(3, "loadPackage(%s, %s)", fileName.c_str(), mountPosition.c_str());
+
+ Common::Archive *zipFile = Common::makeZipArchive(fileName);
+ if (zipFile == NULL) {
+ BS_LOG_ERRORLN("Unable to mount file \"%s\" to \"%s\"", fileName.c_str(), mountPosition.c_str());
+ return false;
+ } else {
+ BS_LOGLN("Package '%s' mounted as '%s'.", fileName.c_str(), mountPosition.c_str());
+ Common::ArchiveMemberList files;
+ zipFile->listMembers(files);
+ debug(3, "Capacity %d", files.size());
+
+ for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it)
+ debug(3, "%s", (*it)->getName().c_str());
+
+ _archiveList.push_front(new ArchiveEntry(zipFile, mountPosition));
+
+ return true;
+ }
+}
+
+bool PackageManager::loadDirectoryAsPackage(const Common::String &directoryName, const Common::String &mountPosition) {
+ Common::FSNode directory(directoryName);
+ Common::Archive *folderArchive = new Common::FSDirectory(directory, 6);
+ if (!directory.exists() || (folderArchive == NULL)) {
+ BS_LOG_ERRORLN("Unable to mount directory \"%s\" to \"%s\".", directoryName.c_str(), mountPosition.c_str());
+ return false;
+ } else {
+ BS_LOGLN("Directory '%s' mounted as '%s'.", directoryName.c_str(), mountPosition.c_str());
+
+ Common::ArchiveMemberList files;
+ folderArchive->listMembers(files);
+ debug(0, "Capacity %d", files.size());
+
+ _archiveList.push_front(new ArchiveEntry(folderArchive, mountPosition));
+
+ return true;
+ }
+}
+
+byte *PackageManager::getFile(const Common::String &fileName, uint *fileSizePtr) {
+ const Common::String B25S_EXTENSION(".b25s");
+ Common::SeekableReadStream *in;
+
+ if (fileName.hasSuffix(B25S_EXTENSION)) {
+ // Savegame loading logic
+ Common::SaveFileManager *sfm = g_system->getSavefileManager();
+ Common::InSaveFile *file = sfm->openForLoading(
+ FileSystemUtil::GetInstance().GetPathFilename(fileName));
+ if (!file) {
+ BS_LOG_ERRORLN("Could not load savegame \"%s\".", fileName.c_str());
+ return 0;
+ }
+
+ if (*fileSizePtr)
+ *fileSizePtr = file->size();
+
+ byte *buffer = new byte[file->size()];
+ file->read(buffer, file->size());
+
+ delete file;
+ return buffer;
+ }
+
+ Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
+ if (!fileNode)
+ return 0;
+ if (!(in = fileNode->createReadStream()))
+ return 0;
+
+ // If the filesize is desired, then output the size
+ if (fileSizePtr)
+ *fileSizePtr = in->size();
+
+ // Read the file
+ byte *buffer = new byte[in->size()];
+ int bytesRead = in->read(buffer, in->size());
+ delete in;
+
+ if (!bytesRead) {
+ delete buffer;
+ return NULL;
+ }
+
+ return buffer;
+}
+
+Common::SeekableReadStream *PackageManager::getStream(const Common::String &fileName) {
+ Common::SeekableReadStream *in;
+ Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
+ if (!fileNode)
+ return 0;
+ if (!(in = fileNode->createReadStream()))
+ return 0;
+
+ return in;
+}
+
+Common::String PackageManager::getCurrentDirectory() {
+ return _currentDirectory;
+}
+
+bool PackageManager::changeDirectory(const Common::String &directory) {
+ // Get the path elements for the file
+ _currentDirectory = normalizePath(directory, _currentDirectory);
+ return true;
+}
+
+Common::String PackageManager::getAbsolutePath(const Common::String &fileName) {
+ return normalizePath(fileName, _currentDirectory);
+}
+
+uint PackageManager::getFileSize(const Common::String &fileName) {
+ Common::SeekableReadStream *in;
+ Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
+ if (!fileNode)
+ return 0;
+ if (!(in = fileNode->createReadStream()))
+ return 0;
+
+ uint fileSize = in->size();
+
+ return fileSize;
+}
+
+uint PackageManager::getFileType(const Common::String &fileName) {
+ warning("STUB: BS_PackageManager::GetFileType(%s)", fileName.c_str());
+
+ //return fileNode.isDirectory() ? BS_PackageManager::FT_DIRECTORY : BS_PackageManager::FT_FILE;
+ return PackageManager::FT_FILE;
+}
+
+bool PackageManager::fileExists(const Common::String &fileName) {
+ Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
+ return fileNode;
+}
+
+int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) {
+ Common::String normalizedFilter = normalizePath(filter, _currentDirectory);
+ int num = 0;
+
+ if (path.size() > 0)
+ warning("STUB: PackageManager::doSearch(<%s>, <%s>, %d)", filter.c_str(), path.c_str(), typeFilter);
+
+ // Loop through checking each archive
+ Common::List<ArchiveEntry *>::iterator i;
+ for (i = _archiveList.begin(); i != _archiveList.end(); ++i) {
+ Common::ArchiveMemberList memberList;
+
+ if (!normalizedFilter.hasPrefix((*i)->_mountPath)) {
+ // The mount path is in different subtree. Skipping
+ continue;
+ }
+
+ // Construct relative path
+ Common::String resFilter(&normalizedFilter.c_str()[(*i)->_mountPath.size()]);
+
+ if ((*i)->archive->listMatchingMembers(memberList, resFilter) == 0)
+ continue;
+
+ // Create a list of the matching names
+ for (Common::ArchiveMemberList::iterator it = memberList.begin(); it != memberList.end(); ++it) {
+ if (((typeFilter & PackageManager::FT_DIRECTORY) && (*it)->getName().hasSuffix("/")) ||
+ ((typeFilter & PackageManager::FT_FILE) && !(*it)->getName().hasSuffix("/"))) {
+
+ // Do not add duplicate files
+ bool found = false;
+ for (Common::ArchiveMemberList::iterator it1 = list.begin(); it1 != list.end(); ++it1) {
+ if ((*it1)->getName() == (*it)->getName()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ list.push_back(*it);
+ num++;
+ }
+ }
+ }
+
+ return num;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h
new file mode 100644
index 0000000000..96f136dd83
--- /dev/null
+++ b/engines/sword25/package/packagemanager.h
@@ -0,0 +1,222 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_PackageManager
+ * -----------------
+ * This is the package manager interface, that contains all the methods that a package manager
+ * must implement.
+ * In the package manager, note the following:
+ * 1. It creates a completely new (virtual) directory tree in the packages and directories
+ * can be mounted.
+ * 2. To seperate elements of a directory path '/' must be used rather than '\'
+ * 3. LoadDirectoryAsPackage should only be used for testing. The final release will be
+ * have all files in packages.
+ *
+ * Autor: Malte Thiesen, $author$
+ */
+
+#ifndef SWORD25_PACKAGE_MANAGER_H
+#define SWORD25_PACKAGE_MANAGER_H
+
+#include "common/archive.h"
+#include "common/array.h"
+#include "common/fs.h"
+#include "common/str.h"
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/kernel/service.h"
+
+namespace Sword25 {
+
+// Class definitions
+
+/**
+ * The Package Manager interface
+ *
+ * 1. It creates a completely new (virtual) directory tree in the packages and directories
+ * can be mounted.
+ * 2. To seperate elements of a directory path '/' must be used rather than '\'
+ * 3. LoadDirectoryAsPackage should only be used for testing. The final release will be
+ * have all files in packages.
+ */
+class PackageManager : public Service {
+private:
+ class ArchiveEntry {
+ public:
+ Common::Archive *archive;
+ Common::String _mountPath;
+
+ ArchiveEntry(Common::Archive *archive_, const Common::String &mountPath_):
+ archive(archive_), _mountPath(mountPath_) {
+ }
+ ~ArchiveEntry() {
+ delete archive;
+ }
+ };
+
+ Common::String _currentDirectory;
+ Common::FSNode _rootFolder;
+ Common::List<ArchiveEntry *> _archiveList;
+
+ Common::ArchiveMemberPtr getArchiveMember(const Common::String &fileName);
+
+public:
+ PackageManager(Kernel *pKernel);
+ ~PackageManager();
+
+ enum FILE_TYPES {
+ FT_DIRECTORY = (1 << 0),
+ FT_FILE = (1 << 1)
+ };
+
+ /**
+ * Mounts the contents of a package in the directory specified in the directory tree.
+ * @param FileName The filename of the package to mount
+ * @param MountPosition The directory name under which the package should be mounted
+ * @return Returns true if the mount was successful, otherwise false.
+ */
+ bool loadPackage(const Common::String &fileName, const Common::String &mountPosition);
+ /**
+ * Mounts the contents of a directory in the specified directory in the directory tree.
+ * @param The name of the directory to mount
+ * @param MountPosition The directory name under which the package should be mounted
+ * @return Returns true if the mount was successful, otherwise false.
+ */
+ bool loadDirectoryAsPackage(const Common::String &directoryName, const Common::String &mountPosition);
+ /**
+ * Downloads a file from the directory tree
+ * @param FileName The filename of the file to load
+ * @param pFileSize Pointer to the variable that will contain the size of the loaded file. The deafult is NULL.
+ * @return Specifies a pointer to the loaded data of the file
+ * @remark The client must not forget to release the data of the file using BE_DELETE_A.
+ */
+ byte *getFile(const Common::String &fileName, uint *pFileSize = NULL);
+
+ /**
+ * Returns a stream from file file from the directory tree
+ * @param FileName The filename of the file to load
+ * @return Pointer to the stream object
+ */
+ Common::SeekableReadStream *getStream(const Common::String &fileName);
+ /**
+ * Downloads an XML file and prefixes it with an XML Version key, since the XML files don't contain it,
+ * and it is required for ScummVM to correctly parse the XML.
+ * @param FileName The filename of the file to load
+ * @param pFileSize Pointer to the variable that will contain the size of the loaded file. The deafult is NULL.
+ * @return Specifies a pointer to the loaded data of the file
+ * @remark The client must not forget to release the data of the file using BE_DELETE_A.
+ */
+ char *getXmlFile(const Common::String &fileName, uint *pFileSize = NULL) {
+ const char *versionStr = "<?xml version=\"1.0\"?>";
+ uint fileSize;
+ char *data = (char *)getFile(fileName, &fileSize);
+ char *result = (char *)malloc(fileSize + strlen(versionStr) + 1);
+ strcpy(result, versionStr);
+ Common::copy(data, data + fileSize, result + strlen(versionStr));
+ result[fileSize + strlen(versionStr)] = '\0';
+
+ delete[] data;
+ if (pFileSize)
+ *pFileSize = fileSize + strlen(versionStr);
+
+ return result;
+ }
+
+ /**
+ * Returns the path to the current directory.
+ * @return Returns a string containing the path to the current directory.
+ * If the path could not be determined, an empty string is returned.
+ * @remark For cutting path elements '\' is used rather than '/' elements.
+ */
+ Common::String getCurrentDirectory();
+ /**
+ * Changes the current directory.
+ * @param Directory The path to the new directory. The path can be relative.
+ * @return Returns true if the operation was successful, otherwise false.
+ * @remark For cutting path elements '\' is used rather than '/' elements.
+ */
+ bool changeDirectory(const Common::String &directory);
+ /**
+ * Returns the absolute path to a file in the directory tree.
+ * @param FileName The filename of the file whose absolute path is to be determined.
+ * These parameters may include both relative and absolute paths.
+ * @return Returns an absolute path to the given file.
+ * @remark For cutting path elements '\' is used rather than '/' elements.
+ */
+ Common::String getAbsolutePath(const Common::String &fileName);
+ /**
+ * Creates a BS_PackageManager::FileSearch object to search for files
+ * @param Filter Specifies the search string. Wildcards of '*' and '?' are allowed
+ * @param Path Specifies the directory that should be searched.
+ * @param TypeFilter A combination of flags BS_PackageManager::FT_DIRECTORY and BS_PackageManager::FT_FILE.
+ * These flags indicate whether to search for files, directories, or both.
+ * The default is BS_PackageManager::FT_DIRECTORY | BS_PackageManager::FT_FILE
+ * @return Specifies a pointer to a BS_PackageManager::FileSearch object, or NULL if no file was found.
+ * @remark Do not forget to delete the object after use.
+ */
+ int doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter = FT_DIRECTORY | FT_FILE);
+
+ /**
+ * Returns a file's size
+ * @param FileName The filename
+ * @return The file size. If an error occurs, then 0xffffffff is returned.
+ * @remarks For files in packages, then uncompressed size is returned.
+ **/
+ uint getFileSize(const Common::String &fileName);
+
+ /**
+ * Returns the type of a file.
+ * @param FileName The filename
+ * @return Returns the file type, either (BS_PackageManager::FT_DIRECTORY
+ * or BS_PackageManager::FT_FILE).
+ * If the file was not found, then 0 is returned.
+ */
+ uint getFileType(const Common::String &fileName);
+
+ /**
+ * Determines whether a file exists
+ * @param FileName The filename
+ * @return Returns true if the file exists, otherwise false.
+ */
+ bool fileExists(const Common::String &FileName);
+
+private:
+ bool registerScriptBindings();
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/package/packagemanager_script.cpp b/engines/sword25/package/packagemanager_script.cpp
new file mode 100644
index 0000000000..cfcea55944
--- /dev/null
+++ b/engines/sword25/package/packagemanager_script.cpp
@@ -0,0 +1,215 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+
+#include "sword25/package/packagemanager.h"
+
+namespace Sword25 {
+
+using namespace Lua;
+
+static PackageManager *getPM() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ PackageManager *pPM = static_cast<PackageManager *>(pKernel->GetService("package"));
+ BS_ASSERT(pPM);
+ return pPM;
+}
+
+static int loadPackage(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushbooleancpp(L, pPM->loadPackage(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
+
+ return 1;
+}
+
+static int loadDirectoryAsPackage(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushbooleancpp(L, pPM->loadDirectoryAsPackage(luaL_checkstring(L, 1), luaL_checkstring(L, 2)));
+
+ return 1;
+}
+
+static int getCurrentDirectory(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushstring(L, pPM->getCurrentDirectory().c_str());
+
+ return 1;
+}
+
+static int changeDirectory(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushbooleancpp(L, pPM->changeDirectory(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+static int getAbsolutePath(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushstring(L, pPM->getAbsolutePath(luaL_checkstring(L, 1)).c_str());
+
+ return 1;
+}
+
+static int getFileSize(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushnumber(L, pPM->getFileSize(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+static int getFileType(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ lua_pushnumber(L, pPM->getFileType(luaL_checkstring(L, 1)));
+
+ return 1;
+}
+
+static void splitSearchPath(const Common::String &path, Common::String &directory, Common::String &filter) {
+ // Scan backwards for a trailing slash
+ const char *sPath = path.c_str();
+ const char *lastSlash = sPath + strlen(sPath) - 1;
+ while ((lastSlash >= sPath) && (*lastSlash != '/')) --lastSlash;
+
+ if (lastSlash >= sPath) {
+ directory = "";
+ filter = path;
+ } else {
+ directory = Common::String(sPath, lastSlash - sPath);
+ filter = Common::String(lastSlash + 1);
+ }
+}
+
+static void doSearch(lua_State *L, const Common::String &path, uint type) {
+ PackageManager *pPM = getPM();
+
+ // Der Packagemanager-Service muss den Suchstring und den Pfad getrennt übergeben bekommen.
+ // Um die Benutzbarkeit zu verbessern sollen Skriptprogrammierer dieses als ein Pfad übergeben können.
+ // Daher muss der übergebene Pfad am letzten Slash aufgesplittet werden.
+ Common::String directory;
+ Common::String filter;
+ splitSearchPath(path, directory, filter);
+
+ // Ergebnistable auf dem Lua-Stack erstellen
+ lua_newtable(L);
+
+ // Suche durchführen und die Namen aller gefundenen Dateien in die Ergebnistabelle einfügen.
+ // Als Indizes werden fortlaufende Nummern verwandt.
+ uint resultNr = 1;
+ Common::ArchiveMemberList list;
+ int numMatches;
+
+ numMatches = pPM->doSearch(list, filter, directory, type);
+ if (numMatches) {
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
+ lua_pushnumber(L, resultNr);
+ lua_pushstring(L, (*it)->getName().c_str());
+ lua_settable(L, -3);
+ resultNr++;
+ }
+ }
+}
+
+static int findFiles(lua_State *L) {
+ doSearch(L, luaL_checkstring(L, 1), PackageManager::FT_FILE);
+ return 1;
+}
+
+static int findDirectories(lua_State *L) {
+ doSearch(L, luaL_checkstring(L, 1), PackageManager::FT_DIRECTORY);
+ return 1;
+}
+
+static int getFileAsString(lua_State *L) {
+ PackageManager *pPM = getPM();
+
+ uint fileSize;
+ char *fileData = (char *)pPM->getFile(luaL_checkstring(L, 1), &fileSize);
+ if (fileData) {
+ lua_pushlstring(L, fileData, fileSize);
+ delete[] fileData;
+
+ return 1;
+ } else
+ return 0;
+}
+
+static int fileExists(lua_State *L) {
+ lua_pushbooleancpp(L, getPM()->fileExists(luaL_checkstring(L, 1)));
+ return 1;
+}
+
+static const char *PACKAGE_LIBRARY_NAME = "Package";
+
+static const luaL_reg PACKAGE_FUNCTIONS[] = {
+ {"LoadPackage", loadPackage},
+ {"LoadDirectoryAsPackage", loadDirectoryAsPackage},
+ {"GetCurrentDirectory", getCurrentDirectory},
+ {"ChangeDirectory", changeDirectory},
+ {"GetAbsolutePath", getAbsolutePath},
+ {"GetFileSize", getFileSize},
+ {"GetFileType", getFileType},
+ {"FindFiles", findFiles},
+ {"FindDirectories", findDirectories},
+ {"GetFileAsString", getFileAsString},
+ {"FileExists", fileExists},
+ {0, 0}
+};
+
+bool PackageManager::registerScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, PACKAGE_LIBRARY_NAME, PACKAGE_FUNCTIONS))
+ return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/script/lua_extensions.cpp b/engines/sword25/script/lua_extensions.cpp
new file mode 100644
index 0000000000..3c0d4570a2
--- /dev/null
+++ b/engines/sword25/script/lua_extensions.cpp
@@ -0,0 +1,75 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/script/luascript.h"
+#include "sword25/script/luabindhelper.h"
+
+namespace Sword25 {
+
+static int warning(lua_State *L) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ luaL_checkstring(L, 1);
+ luaL_where(L, 1);
+ lua_pushstring(L, "WARNING - ");
+ lua_pushvalue(L, 1);
+ lua_concat(L, 3);
+ BS_Log::Log("%s\n", luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return 0;
+}
+
+static const luaL_reg GLOBAL_FUNCTIONS[] = {
+ {"warning", warning},
+ {0, 0}
+};
+
+bool LuaScriptEngine::registerStandardLibExtensions() {
+ lua_State *L = _state;
+ BS_ASSERT(_state);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, "", GLOBAL_FUNCTIONS))
+ return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/script/luabindhelper.cpp b/engines/sword25/script/luabindhelper.cpp
new file mode 100644
index 0000000000..5367854218
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.cpp
@@ -0,0 +1,427 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/luabindhelper.h"
+#include "sword25/script/luascript.h"
+
+#define BS_LOG_PREFIX "LUABINDHELPER"
+
+namespace {
+const char *METATABLES_TABLE_NAME = "__METATABLES";
+const char *PERMANENTS_TABLE_NAME = "Permanents";
+
+bool registerPermanent(lua_State *L, const Common::String &name) {
+ // A C function has to be on the stack
+ if (!lua_iscfunction(L, -1))
+ return false;
+
+ // Make sure that the Permanents-Table is on top of the stack
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ if (lua_isnil(L, -1)) {
+ // Permanents-Table does not yet exist, so it has to be created
+
+ // Pop nil from the stack
+ lua_pop(L, 1);
+
+ // Create Permanents-Table and insert a second reference to it on the stack
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+
+ // Store the Permanents-Table in the registry. The second reference is left
+ // on the stack to be used in the connection
+ lua_setfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+ }
+
+ // C function with the name of an index in the Permanents-Table
+ lua_insert(L, -2);
+ lua_setfield(L, -2, name.c_str());
+
+ // Remove the Permanents-Table from the stack
+ lua_pop(L, 1);
+
+ return true;
+}
+}
+
+namespace Sword25 {
+
+/**
+ * Registers a set of functions into a Lua library.
+ * @param L A pointer to the Lua VM
+ * @param LibName The name of the library.
+ * If this is an empty string, the functions will be added to the global namespace.
+ * @param Functions An array of function pointers along with their names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+bool LuaBindhelper::addFunctionsToLib(lua_State *L, const Common::String &libName, const luaL_reg *functions) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // If the table name is empty, the functions are to be added to the global namespace
+ if (libName.size() == 0) {
+ for (; functions->name; ++functions) {
+ lua_pushstring(L, functions->name);
+ lua_pushcclosure(L, functions->func, 0);
+ lua_settable(L, LUA_GLOBALSINDEX);
+
+ // Function is being permanently registed, so persistence can be ignored
+ lua_pushstring(L, functions->name);
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ registerPermanent(L, functions->name);
+ }
+ } else { // If the table name is not empty, the functions are added to the given table
+ // Ensure that the library table exists
+ if (!createTable(L, libName)) return false;
+
+ // Register each function into the table
+ for (; functions->name; ++functions) {
+ // Function registration
+ lua_pushstring(L, functions->name);
+ lua_pushcclosure(L, functions->func, 0);
+ lua_settable(L, -3);
+
+ // Function is being permanently registed, so persistence can be ignored
+ lua_pushstring(L, functions->name);
+ lua_gettable(L, -2);
+ registerPermanent(L, libName + "." + functions->name);
+ }
+
+ // Remove the library table from the Lua stack
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+/**
+ * Adds a set of constants to the Lua library
+ * @param L A pointer to the Lua VM
+ * @param LibName The name of the library.
+ * If this is an empty string, the functions will be added to the global namespace.
+ * @param Constants An array of the constant values along with their names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+bool LuaBindhelper::addConstantsToLib(lua_State *L, const Common::String &libName, const lua_constant_reg *constants) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // If the table is empty, the constants are added to the global namespace
+ if (libName.size() == 0) {
+ for (; constants->Name; ++constants) {
+ lua_pushstring(L, constants->Name);
+ lua_pushnumber(L, constants->Value);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+ // If the table name is nto empty, the constants are added to that table
+ else {
+ // Ensure that the library table exists
+ if (!createTable(L, libName)) return false;
+
+ // Register each constant in the table
+ for (; constants->Name; ++constants) {
+ lua_pushstring(L, constants->Name);
+ lua_pushnumber(L, constants->Value);
+ lua_settable(L, -3);
+ }
+
+ // Remove the library table from the Lua stack
+ lua_pop(L, 1);
+ }
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+/**
+ * Adds a set of methods to a Lua class
+ * @param L A pointer to the Lua VM
+ * @param ClassName The name of the class
+ * When the class name specified does not exist, it is created.
+ * @param Methods An array of function pointers along with their method names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+bool LuaBindhelper::addMethodsToClass(lua_State *L, const Common::String &className, const luaL_reg *methods) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Load the metatable onto the Lua stack
+ if (!getMetatable(L, className)) return false;
+
+ // Register each method in the Metatable
+ for (; methods->name; ++methods) {
+ lua_pushstring(L, methods->name);
+ lua_pushcclosure(L, methods->func, 0);
+ lua_settable(L, -3);
+
+ // Function is being permanently registed, so persistence can be ignored
+ lua_pushstring(L, methods->name);
+ lua_gettable(L, -2);
+ registerPermanent(L, className + "." + methods->name);
+ }
+
+ // Remove the metatable from the stack
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+/**
+ * Sets the garbage collector callback method when items of a particular class are deleted
+ * @param L A pointer to the Lua VM
+ * @param ClassName The name of the class
+ * When the class name specified does not exist, it is created.
+ * @param GCHandler A function pointer
+ * @return Returns true if successful, otherwise false.
+ */
+bool LuaBindhelper::setClassGCHandler(lua_State *L, const Common::String &className, lua_CFunction GCHandler) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(L);
+#endif
+
+ // Load the metatable onto the Lua stack
+ if (!getMetatable(L, className)) return false;
+
+ // Add the GC handler to the Metatable
+ lua_pushstring(L, "__gc");
+ lua_pushcclosure(L, GCHandler, 0);
+ lua_settable(L, -3);
+
+ // Function is being permanently registed, so persistence can be ignored
+ lua_pushstring(L, "__gc");
+ lua_gettable(L, -2);
+ registerPermanent(L, className + ".__gc");
+
+ // Remove the metatable from the stack
+ lua_pop(L, 1);
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(L));
+#endif
+
+ return true;
+}
+
+} // End of namespace Sword25
+
+namespace {
+void pushMetatableTable(lua_State *L) {
+ // Push the Metatable table onto the stack
+ lua_getglobal(L, METATABLES_TABLE_NAME);
+
+ // If the table doesn't yet exist, it must be created
+ if (lua_isnil(L, -1)) {
+ // Pop nil from stack
+ lua_pop(L, 1);
+
+ // New table has been created, so add it to the global table and leave reference on stack
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, METATABLES_TABLE_NAME);
+ }
+}
+}
+
+namespace Sword25 {
+
+bool LuaBindhelper::getMetatable(lua_State *L, const Common::String &tableName) {
+ // Push the Metatable table onto the stack
+ pushMetatableTable(L);
+
+ // Versuchen, die gewünschte Metatabelle auf den Stack zu legen. Wenn sie noch nicht existiert, muss sie erstellt werden.
+ lua_getfield(L, -1, tableName.c_str());
+ if (lua_isnil(L, -1)) {
+ // Pop nil from stack
+ lua_pop(L, 1);
+
+ // Create new table
+ lua_newtable(L);
+
+ // Set the __index field in the table
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+
+ // Flag the table as persisted. This ensures that objects within this table get stored
+ lua_pushbooleancpp(L, true);
+ lua_setfield(L, -2, "__persist");
+
+ // Set the table name and push it onto the stack
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, tableName.c_str());
+ }
+
+ // Remove the Metatable table from the stack
+ lua_remove(L, -2);
+
+ return true;
+}
+
+bool LuaBindhelper::createTable(lua_State *L, const Common::String &tableName) {
+ const char *partBegin = tableName.c_str();
+
+ while (partBegin - tableName.c_str() < (int)tableName.size()) {
+ const char *partEnd = strchr(partBegin, '.');
+ if (!partEnd)
+ partEnd = partBegin + strlen(partBegin);
+ Common::String subTableName(partBegin, partEnd);
+
+ // Tables with an empty string as the name are not allowed
+ if (subTableName.size() == 0)
+ return false;
+
+ // Verify that the table with the name already exists
+ // The first round will be searched in the global namespace, with later passages
+ // in the corresponding parent table in the stack
+ if (partBegin == tableName.c_str()) {
+ lua_pushstring(L, subTableName.c_str());
+ lua_gettable(L, LUA_GLOBALSINDEX);
+ } else {
+ lua_pushstring(L, subTableName.c_str());
+ lua_gettable(L, -2);
+ if (!lua_isnil(L, -1))
+ lua_remove(L, -2);
+ }
+
+ // If it doesn't exist, create table
+ if (lua_isnil(L, -1)) {
+ // Pop nil from stack
+ lua_pop(L, 1);
+
+ // Create new table
+ lua_newtable(L);
+ lua_pushstring(L, subTableName.c_str());
+ lua_pushvalue(L, -2);
+ if (partBegin == tableName.c_str())
+ lua_settable(L, LUA_GLOBALSINDEX);
+ else {
+ lua_settable(L, -4);
+ lua_remove(L, -2);
+ }
+ }
+
+ partBegin = partEnd + 1;
+ }
+
+ return true;
+}
+
+} // End of namespace Sword25
+
+namespace {
+Common::String getLuaValueInfo(lua_State *L, int stackIndex) {
+ switch (lua_type(L, stackIndex)) {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, stackIndex));
+ break;
+
+ case LUA_TSTRING:
+ lua_pushfstring(L, "\"%s\"", lua_tostring(L, stackIndex));
+ break;
+
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, stackIndex) ? "true" : "false"));
+ break;
+
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+
+ default:
+ lua_pushfstring(L, "%s: %p", luaL_typename(L, stackIndex), lua_topointer(L, stackIndex));
+ break;
+ }
+
+ Common::String result(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ return result;
+}
+}
+
+namespace Sword25 {
+
+Common::String LuaBindhelper::stackDump(lua_State *L) {
+ Common::String oss;
+
+ int i = lua_gettop(L);
+ oss += "------------------- Stack Dump -------------------\n";
+
+ while (i) {
+ oss += i + ": " + getLuaValueInfo(L, i) + "\n";
+ i--;
+ }
+
+ oss += "-------------- Stack Dump Finished ---------------\n";
+
+ return oss;
+}
+
+Common::String LuaBindhelper::tableDump(lua_State *L) {
+ Common::String oss;
+
+ oss += "------------------- Table Dump -------------------\n";
+
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ // Get the value of the current element on top of the stack, including the index
+ oss += getLuaValueInfo(L, -2) + " : " + getLuaValueInfo(L, -1) + "\n";
+
+ // Pop value from the stack. The index is then ready for the next call to lua_next()
+ lua_pop(L, 1);
+ }
+
+ oss += "-------------- Table Dump Finished ---------------\n";
+
+ return oss;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/script/luabindhelper.h b/engines/sword25/script/luabindhelper.h
new file mode 100644
index 0000000000..0dbaaa3186
--- /dev/null
+++ b/engines/sword25/script/luabindhelper.h
@@ -0,0 +1,128 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_LUABINDHELPER_H
+#define SWORD25_LUABINDHELPER_H
+
+#include "sword25/kernel/common.h"
+
+namespace Lua {
+
+extern "C"
+{
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+}
+
+}
+
+using namespace Lua;
+
+namespace Sword25 {
+
+#define lua_pushbooleancpp(L, b) (lua_pushboolean(L, b ? 1 : 0))
+#define lua_tobooleancpp(L, i) (lua_toboolean(L, i) == 0 ? false : true)
+
+struct lua_constant_reg {
+ const char *Name;
+ lua_Number Value;
+};
+
+class LuaBindhelper {
+public:
+ /**
+ * Registers a set of functions into a Lua library.
+ * @param L A pointer to the Lua VM
+ * @param LibName The name of the library.
+ * If this is an empty string, the functions will be added to the global namespace.
+ * @param Functions An array of function pointers along with their names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+ static bool addFunctionsToLib(lua_State *L, const Common::String &libName, const luaL_reg *functions);
+
+ /**
+ * Adds a set of constants to the Lua library
+ * @param L A pointer to the Lua VM
+ * @param LibName The name of the library.
+ * If this is an empty string, the functions will be added to the global namespace.
+ * @param Constants An array of the constant values along with their names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+ static bool addConstantsToLib(lua_State *L, const Common::String &libName, const lua_constant_reg *constants);
+
+ /**
+ * Adds a set of methods to a Lua class
+ * @param L A pointer to the Lua VM
+ * @param ClassName The name of the class
+ * When the class name specified does not exist, it is created.
+ * @param Methods An array of function pointers along with their method names.
+ * The array must be terminated with the enry (0, 0)
+ * @return Returns true if successful, otherwise false.
+ */
+ static bool addMethodsToClass(lua_State *L, const Common::String &className, const luaL_reg *methods);
+
+ /**
+ * Sets the garbage collector callback method when items of a particular class are deleted
+ * @param L A pointer to the Lua VM
+ * @param ClassName The name of the class
+ * When the class name specified does not exist, it is created.
+ * @param GCHandler A function pointer
+ * @return Returns true if successful, otherwise false.
+ */
+ static bool setClassGCHandler(lua_State *L, const Common::String &className, lua_CFunction GCHandler);
+
+ /**
+ * Returns a string containing a stack dump of the Lua stack
+ * @param L A pointer to the Lua VM
+ */
+ static Common::String stackDump(lua_State *L);
+
+ /**
+ * Returns a string that describes the contents of a table
+ * @param L A pointer to the Lua VM
+ * @remark The table must be on the Lua stack to be read out.
+ */
+ static Common::String tableDump(lua_State *L);
+
+ static bool getMetatable(lua_State *L, const Common::String &tableName);
+
+private:
+ static bool createTable(lua_State *L, const Common::String &tableName);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/script/luacallback.cpp b/engines/sword25/script/luacallback.cpp
new file mode 100644
index 0000000000..6d2e634632
--- /dev/null
+++ b/engines/sword25/script/luacallback.cpp
@@ -0,0 +1,180 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "sword25/script/luacallback.h"
+#include "sword25/script/luabindhelper.h"
+
+namespace Lua {
+
+extern "C"
+{
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lauxlib.h"
+}
+
+const char *CALLBACKTABLE_NAME = "__CALLBACKS";
+
+}
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "LUA"
+
+LuaCallback::LuaCallback(lua_State *L) {
+ // Create callback table
+ lua_newtable(L);
+ lua_setglobal(L, CALLBACKTABLE_NAME);
+}
+
+LuaCallback::~LuaCallback() {
+}
+
+void LuaCallback::registerCallbackFunction(lua_State *L, uint objectHandle) {
+ BS_ASSERT(lua_isfunction(L, -1));
+ ensureObjectCallbackTableExists(L, objectHandle);
+
+ // Store function in the callback object table store
+ lua_pushvalue(L, -2);
+ luaL_ref(L, -2);
+
+ // Pop the function and object callback table from the stack
+ lua_pop(L, 2);
+}
+
+void LuaCallback::unregisterCallbackFunction(lua_State *L, uint objectHandle) {
+ BS_ASSERT(lua_isfunction(L, -1));
+ ensureObjectCallbackTableExists(L, objectHandle);
+
+ // Iterate over all elements of the object callback table and remove the function from it
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ // The value of the current element is the top of the stack, including the index
+
+ // If the value is identical to the function parameters, it is removed from the table
+ if (lua_equal(L, -1, -4)) {
+ lua_pushvalue(L, -2);
+ lua_pushnil(L);
+ lua_settable(L, -5);
+
+ // The function was found, iteration can be stopped
+ lua_pop(L, 2);
+ break;
+ } else {
+ // Pop value from the stack. The index is then ready for the next call to lua_next()
+ lua_pop(L, 1);
+ }
+ }
+
+ // Function and object table are popped from the stack
+ lua_pop(L, 2);
+}
+
+void LuaCallback::removeAllObjectCallbacks(lua_State *L, uint objectHandle) {
+ pushCallbackTable(L);
+
+ // Remove the object callback from the callback table
+ lua_pushnumber(L, objectHandle);
+ lua_pushnil(L);
+ lua_settable(L, -3);
+
+ lua_pop(L, 1);
+}
+
+void LuaCallback::invokeCallbackFunctions(lua_State *L, uint objectHandle) {
+ ensureObjectCallbackTableExists(L, objectHandle);
+
+ // Iterate through the table and perform all the callbacks
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ // The value of the current element is at the top of the stack, including the index
+
+ // If the value is a function, execute it
+ if (lua_type(L, -1) == LUA_TFUNCTION) {
+ // Pre-Function Call
+ // Derived classes can function in this parameter onto the stack.
+ // The return value indicates the number of parameters
+ int argumentCount = preFunctionInvokation(L);
+
+ // Lua_pcall the function and the parameters pop themselves from the stack
+ if (lua_pcall(L, argumentCount, 0, 0) != 0) {
+ // An error has occurred
+ BS_LOG_ERRORLN("An error occured executing a callback function: %s", lua_tostring(L, -1));
+
+ // Pop error message from the stack
+ lua_pop(L, 1);
+ }
+ } else {
+ // Pop value from the stack. The index is then ready for the next call to lua_next()
+ lua_pop(L, 1);
+ }
+ }
+}
+
+void LuaCallback::ensureObjectCallbackTableExists(lua_State *L, uint objectHandle) {
+ pushObjectCallbackTable(L, objectHandle);
+
+ // If the table is nil, it must first be created
+ if (lua_isnil(L, -1)) {
+ // Pop nil from stack
+ lua_pop(L, 1);
+
+ pushCallbackTable(L);
+
+ // Create the table, and put the objectHandle into it
+ lua_newtable(L);
+ lua_pushnumber(L, objectHandle);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4);
+
+ // Pop the callback table from the stack
+ lua_remove(L, -2);
+ }
+}
+
+void LuaCallback::pushCallbackTable(lua_State *L) {
+ lua_getglobal(L, CALLBACKTABLE_NAME);
+}
+
+void LuaCallback::pushObjectCallbackTable(lua_State *L, uint objectHandle) {
+ pushCallbackTable(L);
+
+ // Push Object Callback table onto the stack
+ lua_pushnumber(L, objectHandle);
+ lua_gettable(L, -2);
+
+ // Pop the callback table from the stack
+ lua_remove(L, -2);
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/script/luacallback.h b/engines/sword25/script/luacallback.h
new file mode 100644
index 0000000000..0a5dec17d9
--- /dev/null
+++ b/engines/sword25/script/luacallback.h
@@ -0,0 +1,78 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_LUACALLBACK_H
+#define SWORD25_LUACALLBACK_H
+
+#include "sword25/kernel/common.h"
+
+namespace Lua {
+
+struct lua_State;
+
+}
+
+using namespace Lua;
+
+namespace Sword25 {
+
+class LuaCallback {
+public:
+ LuaCallback(lua_State *L);
+ virtual ~LuaCallback();
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void registerCallbackFunction(lua_State *L, uint objectHandle);
+
+ // Funktion muss auf dem Lua-Stack liegen.
+ void unregisterCallbackFunction(lua_State *L, uint objectHandle);
+
+ void removeAllObjectCallbacks(lua_State *L, uint objectHandle);
+
+ void invokeCallbackFunctions(lua_State *L, uint objectHandle);
+
+protected:
+ virtual int preFunctionInvokation(lua_State *L) {
+ return 0;
+ }
+
+private:
+ void ensureObjectCallbackTableExists(lua_State *L, uint objectHandle);
+ void pushCallbackTable(lua_State *L);
+ void pushObjectCallbackTable(lua_State *L, uint objectHandle);
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp
new file mode 100644
index 0000000000..82166f7c25
--- /dev/null
+++ b/engines/sword25/script/luascript.cpp
@@ -0,0 +1,569 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "LUA"
+
+#include "common/array.h"
+#include "common/debug-channels.h"
+
+#include "sword25/sword25.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/script/luascript.h"
+#include "sword25/script/luabindhelper.h"
+
+#include "sword25/kernel/outputpersistenceblock.h"
+#include "sword25/kernel/inputpersistenceblock.h"
+
+namespace Lua {
+
+extern "C" {
+#include "sword25/util/lua/lua.h"
+#include "sword25/util/lua/lualib.h"
+#include "sword25/util/lua/lauxlib.h"
+#include "sword25/util/pluto/pluto.h"
+}
+
+}
+
+namespace Sword25 {
+
+using namespace Lua;
+
+LuaScriptEngine::LuaScriptEngine(Kernel *KernelPtr) :
+ ScriptEngine(KernelPtr),
+ _state(0),
+ _pcallErrorhandlerRegistryIndex(0) {
+}
+
+LuaScriptEngine::~LuaScriptEngine() {
+ // Lua de-initialisation
+ if (_state)
+ lua_close(_state);
+}
+
+Service *LuaScriptEngine_CreateObject(Kernel *KernelPtr) {
+ return new LuaScriptEngine(KernelPtr);
+}
+
+namespace {
+int panicCB(lua_State *L) {
+ BS_LOG_ERRORLN("Lua panic. Error message: %s", lua_isnil(L, -1) ? "" : lua_tostring(L, -1));
+ return 0;
+}
+
+void debugHook(lua_State *L, lua_Debug *ar) {
+ if (!lua_getinfo(L, "Sn", ar))
+ return;
+
+ debug("LUA: %s %s: %s %d", ar->namewhat, ar->name, ar->short_src, ar->currentline);
+}
+}
+
+bool LuaScriptEngine::init() {
+ // Lua-State initialisation, as well as standard libaries initialisation
+ _state = luaL_newstate();
+ if (!_state || ! registerStandardLibs() || !registerStandardLibExtensions()) {
+ BS_LOG_ERRORLN("Lua could not be initialized.");
+ return false;
+ }
+
+ // Register panic callback function
+ lua_atpanic(_state, panicCB);
+
+ // Error handler for lua_pcall calls
+ // The code below contains a local error handler function
+ const char errorHandlerCode[] =
+ "local function ErrorHandler(message) "
+ " return message .. '\\n' .. debug.traceback('', 2) "
+ "end "
+ "return ErrorHandler";
+
+ // Compile the code
+ if (luaL_loadbuffer(_state, errorHandlerCode, strlen(errorHandlerCode), "PCALL ERRORHANDLER") != 0) {
+ // An error occurred, so dislay the reason and exit
+ BS_LOG_ERRORLN("Couldn't compile luaL_pcall errorhandler:\n%s", lua_tostring(_state, -1));
+ lua_pop(_state, 1);
+
+ return false;
+ }
+ // Running the code, the error handler function sets the top of the stack
+ if (lua_pcall(_state, 0, 1, 0) != 0) {
+ // An error occurred, so dislay the reason and exit
+ BS_LOG_ERRORLN("Couldn't prepare luaL_pcall errorhandler:\n%s", lua_tostring(_state, -1));
+ lua_pop(_state, 1);
+
+ return false;
+ }
+
+ // Place the error handler function in the Lua registry, and remember the index
+ _pcallErrorhandlerRegistryIndex = luaL_ref(_state, LUA_REGISTRYINDEX);
+
+ // Initialise the Pluto-Persistence library
+ luaopen_pluto(_state);
+ lua_pop(_state, 1);
+
+ // Initialize debugging callback
+ if (DebugMan.isDebugChannelEnabled(kDebugScript)) {
+ int mask = 0;
+ if ((gDebugLevel & 1) != 0)
+ mask |= LUA_MASKCALL;
+ if ((gDebugLevel & 2) != 0)
+ mask |= LUA_MASKRET;
+ if ((gDebugLevel & 4) != 0)
+ mask |= LUA_MASKLINE;
+
+ if (mask != 0)
+ lua_sethook(_state, debugHook, mask, 0);
+ }
+
+ BS_LOGLN("Lua initialized.");
+
+ return true;
+}
+
+bool LuaScriptEngine::executeFile(const Common::String &fileName) {
+#ifdef DEBUG
+ int __startStackDepth = lua_gettop(_state);
+#endif
+ debug(2, "LuaScriptEngine::executeFile(%s)", fileName.c_str());
+
+ // Get a pointer to the package manager
+ PackageManager *pPackage = static_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(pPackage);
+
+ // File read
+ uint fileSize;
+ byte *fileData = pPackage->getFile(fileName, &fileSize);
+ if (!fileData) {
+ BS_LOG_ERRORLN("Couldn't read \"%s\".", fileName.c_str());
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(_state));
+#endif
+ return false;
+ }
+
+ // Run the file content
+ if (!executeBuffer(fileData, fileSize, "@" + pPackage->getAbsolutePath(fileName))) {
+ // Release file buffer
+ delete[] fileData;
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(_state));
+#endif
+ return false;
+ }
+
+ // Release file buffer
+ delete[] fileData;
+
+#ifdef DEBUG
+ BS_ASSERT(__startStackDepth == lua_gettop(_state));
+#endif
+
+ return true;
+}
+
+bool LuaScriptEngine::executeString(const Common::String &code) {
+ return executeBuffer((byte *)code.c_str(), code.size(), "???");
+}
+
+namespace {
+
+void removeForbiddenFunctions(lua_State *L) {
+ static const char *FORBIDDEN_FUNCTIONS[] = {
+ "dofile",
+ 0
+ };
+
+ const char **iterator = FORBIDDEN_FUNCTIONS;
+ while (*iterator) {
+ lua_pushnil(L);
+ lua_setfield(L, LUA_GLOBALSINDEX, *iterator);
+ ++iterator;
+ }
+}
+}
+
+bool LuaScriptEngine::registerStandardLibs() {
+ luaL_openlibs(_state);
+ removeForbiddenFunctions(_state);
+ return true;
+}
+
+bool LuaScriptEngine::executeBuffer(const byte *data, uint size, const Common::String &name) const {
+ // Compile buffer
+ if (luaL_loadbuffer(_state, (const char *)data, size, name.c_str()) != 0) {
+ BS_LOG_ERRORLN("Couldn't compile \"%s\":\n%s", name.c_str(), lua_tostring(_state, -1));
+ lua_pop(_state, 1);
+
+ return false;
+ }
+
+ // Error handling function to be executed after the function is put on the stack
+ lua_rawgeti(_state, LUA_REGISTRYINDEX, _pcallErrorhandlerRegistryIndex);
+ lua_insert(_state, -2);
+
+ // Run buffer contents
+ if (lua_pcall(_state, 0, 0, -2) != 0) {
+ BS_LOG_ERRORLN("An error occured while executing \"%s\":\n%s.",
+ name.c_str(),
+ lua_tostring(_state, -1));
+ lua_pop(_state, 2);
+
+ return false;
+ }
+
+ // Remove the error handler function from the stack
+ lua_pop(_state, 1);
+
+ return true;
+}
+
+void LuaScriptEngine::setCommandLine(const Common::StringArray &commandLineParameters) {
+ lua_newtable(_state);
+
+ for (size_t i = 0; i < commandLineParameters.size(); ++i) {
+ lua_pushnumber(_state, i + 1);
+ lua_pushstring(_state, commandLineParameters[i].c_str());
+ lua_settable(_state, -3);
+ }
+
+ lua_setglobal(_state, "CommandLine");
+}
+
+namespace {
+const char *PERMANENTS_TABLE_NAME = "Permanents";
+
+// This array contains the name of global Lua objects that should not be persisted
+const char *STANDARD_PERMANENTS[] = {
+ "string",
+ "xpcall",
+ "package",
+ "tostring",
+ "print",
+ "os",
+ "unpack",
+ "require",
+ "getfenv",
+ "setmetatable",
+ "next",
+ "assert",
+ "tonumber",
+ "io",
+ "rawequal",
+ "collectgarbage",
+ "getmetatable",
+ "module",
+ "rawset",
+ "warning",
+ "math",
+ "debug",
+ "pcall",
+ "table",
+ "newproxy",
+ "type",
+ "coroutine",
+ "select",
+ "gcinfo",
+ "pairs",
+ "rawget",
+ "loadstring",
+ "ipairs",
+ "_VERSION",
+ "setfenv",
+ "load",
+ "error",
+ "loadfile",
+
+ "pairs_next",
+ "ipairs_next",
+ "pluto",
+ "Cfg",
+ "Translator",
+ "Persistence",
+ "CommandLine",
+ 0
+};
+
+enum PERMANENT_TABLE_TYPE {
+ PTT_PERSIST,
+ PTT_UNPERSIST
+};
+
+bool pushPermanentsTable(lua_State *L, PERMANENT_TABLE_TYPE tableType) {
+ // Permanents-Table
+ lua_newtable(L);
+
+ // All standard permanents are inserted into this table
+ uint Index = 0;
+ while (STANDARD_PERMANENTS[Index]) {
+ // Permanents are placed onto the stack; if it does not exist, it is simply ignored
+ lua_getglobal(L, STANDARD_PERMANENTS[Index]);
+ if (!lua_isnil(L, -1)) {
+ // Name of the element as a unique value on the stack
+ lua_pushstring(L, STANDARD_PERMANENTS[Index]);
+
+ // If it is loaded, then it can be used
+ // In this case, the position of name and object are reversed on the stack
+ if (tableType == PTT_UNPERSIST)
+ lua_insert(L, -2);
+
+ // Make an entry in the table
+ lua_settable(L, -3);
+ } else {
+ // Pop nil value from stack
+ lua_pop(L, 1);
+ }
+
+ ++Index;
+ }
+
+ // All registered C functions to be inserted into the table
+ // BS_LuaBindhelper places in the register a table in which all registered C functions
+ // are stored
+
+ // Table is put on the stack
+ lua_getfield(L, LUA_REGISTRYINDEX, PERMANENTS_TABLE_NAME);
+
+ if (!lua_isnil(L, -1)) {
+ // Iterate over all elements of the table
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ // Value and index duplicated on the stack and changed in the sequence
+ lua_pushvalue(L, -1);
+ lua_pushvalue(L, -3);
+
+ // If it is loaded, then it can be used
+ // In this case, the position of name and object are reversed on the stack
+ if (tableType == PTT_UNPERSIST)
+ lua_insert(L, -2);
+
+ // Make an entry in the results table
+ lua_settable(L, -6);
+
+ // Pop value from the stack. The index is then ready for the next call to lua_next()
+ lua_pop(L, 1);
+ }
+ }
+
+ // Pop the C-Permanents table from the stack
+ lua_pop(L, 1);
+
+ // coroutine.yield must be registered in the extra-Permanents table because they
+ // are inactive coroutine C functions on the stack
+
+ // Function coroutine.yield placed on the stack
+ lua_getglobal(L, "coroutine");
+ lua_pushstring(L, "yield");
+ lua_gettable(L, -2);
+
+ // Store coroutine.yield with it's own unique value in the Permanents table
+ lua_pushstring(L, "coroutine.yield");
+
+ if (tableType == PTT_UNPERSIST)
+ lua_insert(L, -2);
+
+ lua_settable(L, -4);
+
+ // Coroutine table is popped from the stack
+ lua_pop(L, 1);
+
+ return true;
+}
+}
+
+namespace {
+int chunkwriter(lua_State *L, const void *p, size_t sz, void *ud) {
+ Common::Array<byte> & chunkData = *reinterpret_cast<Common::Array<byte> * >(ud);
+ const byte *buffer = reinterpret_cast<const byte *>(p);
+
+ while (sz--)
+ chunkData.push_back(*buffer++);
+
+ return 1;
+}
+}
+
+bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) {
+ // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters
+ lua_settop(_state, 0);
+
+ // Garbage Collection erzwingen.
+ lua_gc(_state, LUA_GCCOLLECT, 0);
+
+ // Permanents-Table is set on the stack
+ // pluto_persist expects these two items on the Lua stack
+ pushPermanentsTable(_state, PTT_PERSIST);
+ lua_getglobal(_state, "_G");
+
+ // Lua persists and stores the data in a Common::Array
+ Common::Array<byte> chunkData;
+ pluto_persist(_state, chunkwriter, &chunkData);
+
+ // Persistenzdaten in den Writer schreiben.
+ writer.write(&chunkData[0], chunkData.size());
+
+ // Die beiden Tabellen vom Stack nehmen.
+ lua_pop(_state, 2);
+
+ return true;
+}
+
+namespace {
+
+struct ChunkreaderData {
+ void *BufferPtr;
+ size_t Size;
+ bool BufferReturned;
+};
+
+const char *chunkreader(lua_State *L, void *ud, size_t *sz) {
+ ChunkreaderData &cd = *reinterpret_cast<ChunkreaderData *>(ud);
+
+ if (!cd.BufferReturned) {
+ cd.BufferReturned = true;
+ *sz = cd.Size;
+ return reinterpret_cast<const char *>(cd.BufferPtr);
+ } else {
+ return 0;
+ }
+}
+
+void clearGlobalTable(lua_State *L, const char **exceptions) {
+ // Iterate over all elements of the global table
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ // Now the value and the index of the current element is on the stack
+ // This value does not interest us, so it is popped from the stack
+ lua_pop(L, 1);
+
+ // Determine whether the item is set to nil, so you want to remove from the global table.
+ // For this will determine whether the element name is a string and is present in
+ // the list of exceptions
+ bool setElementToNil = true;
+ if (lua_isstring(L, -1)) {
+ const char *indexString = lua_tostring(L, -1);
+ const char **exceptionsWalker = exceptions;
+ while (*exceptionsWalker) {
+ if (strcmp(indexString, *exceptionsWalker) == 0)
+ setElementToNil = false;
+ ++exceptionsWalker;
+ }
+ }
+
+ // If the above test showed that the item should be removed, it is removed by setting the value to nil.
+ if (setElementToNil) {
+ lua_pushvalue(L, -1);
+ lua_pushnil(L);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ }
+ }
+
+ // Pop the Global table from the stack
+ lua_pop(L, 1);
+
+ // Perform garbage collection, so that all removed elements are deleted
+ lua_gc(L, LUA_GCCOLLECT, 0);
+}
+}
+
+bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) {
+ // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters
+ lua_settop(_state, 0);
+
+ // Permanents table is placed on the stack. This has already happened at this point, because
+ // to create the table all permanents must be accessible. This is the case only for the
+ // beginning of the function, because the global table is emptied below
+ pushPermanentsTable(_state, PTT_UNPERSIST);
+
+ // All items from global table of _G and __METATABLES are removed.
+ // After a garbage collection is performed, and thus all managed objects deleted
+
+ // __METATABLES is not immediately removed becausen the Metatables are needed
+ // for the finalisers of objects.
+ static const char *clearExceptionsFirstPass[] = {
+ "_G",
+ "__METATABLES",
+ 0
+ };
+ clearGlobalTable(_state, clearExceptionsFirstPass);
+
+ // In the second pass, the Metatables are removed
+ static const char *clearExceptionsSecondPass[] = {
+ "_G",
+ 0
+ };
+ clearGlobalTable(_state, clearExceptionsSecondPass);
+
+ // Persisted Lua data
+ Common::Array<byte> chunkData;
+ reader.read(chunkData);
+
+ // Chunk-Reader initialisation. It is used with pluto_unpersist to restore read data
+ ChunkreaderData cd;
+ cd.BufferPtr = &chunkData[0];
+ cd.Size = chunkData.size();
+ cd.BufferReturned = false;
+
+ pluto_unpersist(_state, chunkreader, &cd);
+
+ // Permanents-Table is removed from stack
+ lua_remove(_state, -2);
+
+ // The read elements in the global table about
+ lua_pushnil(_state);
+ while (lua_next(_state, -2) != 0) {
+ // The referenec to the global table (_G) must not be overwritten, or ticks from Lua total
+ bool isGlobalReference = lua_isstring(_state, -2) && strcmp(lua_tostring(_state, -2), "_G") == 0;
+ if (!isGlobalReference) {
+ lua_pushvalue(_state, -2);
+ lua_pushvalue(_state, -2);
+
+ lua_settable(_state, LUA_GLOBALSINDEX);
+ }
+
+ // Pop value from the stack. The index is then ready for the next call to lua_next()
+ lua_pop(_state, 1);
+ }
+
+ // The table with the loaded data is popped from the stack
+ lua_pop(_state, 1);
+
+ // Force garbage collection
+ lua_gc(_state, LUA_GCCOLLECT, 0);
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/script/luascript.h b/engines/sword25/script/luascript.h
new file mode 100644
index 0000000000..f557ae45f1
--- /dev/null
+++ b/engines/sword25/script/luascript.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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_LUASCRIPT_H
+#define SWORD25_LUASCRIPT_H
+
+#include "common/str.h"
+#include "common/str-array.h"
+#include "sword25/kernel/common.h"
+#include "sword25/script/script.h"
+
+namespace Lua {
+
+struct lua_State;
+
+}
+
+using namespace Lua;
+
+namespace Sword25 {
+
+class Kernel;
+
+class LuaScriptEngine : public ScriptEngine {
+public:
+ LuaScriptEngine(Kernel *KernelPtr);
+ virtual ~LuaScriptEngine();
+
+ /**
+ * Initialises the scripting engine
+ * @return Returns true if successful, otherwise false.
+ */
+ virtual bool init();
+
+ /**
+ * Loads a script file and executes it
+ * @param FileName The filename of the script
+ * @return Returns true if successful, otherwise false.
+ */
+ virtual bool executeFile(const Common::String &fileName);
+
+ /**
+ * Execute a string of script code
+ * @param Code A string of script code
+ * @return Returns true if successful, otherwise false.
+ */
+ virtual bool executeString(const Common::String &code);
+
+ /**
+ * Returns a pointer to the main object of the scripting language
+ * @remark Using this method breaks the encapsulation of the language
+ */
+ virtual void *getScriptObject() {
+ return _state;
+ }
+
+ /**
+ * Makes the command line parameters for the scripting environment available
+ * @param CommandLineParameters An array containing all the command line parameters
+ * @remark How the command line parameters will be used by scripts is
+ * dependant on the particular implementation.
+ */
+ virtual void setCommandLine(const Common::StringArray &commandLineParameters);
+
+ /**
+ * @remark The Lua stack is cleared by this method
+ */
+ virtual bool persist(OutputPersistenceBlock &writer);
+ /**
+ * @remark The Lua stack is cleared by this method
+ */
+ virtual bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ lua_State *_state;
+ int _pcallErrorhandlerRegistryIndex;
+
+ bool registerStandardLibs();
+ bool registerStandardLibExtensions();
+ bool executeBuffer(const byte *data, uint size, const Common::String &name) const;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/script/script.h b/engines/sword25/script/script.h
new file mode 100644
index 0000000000..2f72cf0cc8
--- /dev/null
+++ b/engines/sword25/script/script.h
@@ -0,0 +1,96 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_SCRIPT_H
+#define SWORD25_SCRIPT_H
+
+#include "common/array.h"
+#include "common/str.h"
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/service.h"
+#include "sword25/kernel/persistable.h"
+
+namespace Sword25 {
+
+class Kernel;
+class OutputPersistenceBlock;
+class BS_InputPersistenceBlock;
+
+class ScriptEngine : public Service, public Persistable {
+public:
+ ScriptEngine(Kernel *KernelPtr) : Service(KernelPtr) {};
+ virtual ~ScriptEngine() {};
+
+ // -----------------------------------------------------------------------------
+ // This method must be implemented by the script engine
+ // -----------------------------------------------------------------------------
+
+ /**
+ * Initialises the scrip tengine. Returns true if successful, false otherwise.
+ */
+ virtual bool init() = 0;
+
+ /**
+ * Loads a script file and executes it.
+ * @param FileName The script filename
+ */
+ virtual bool executeFile(const Common::String &fileName) = 0;
+
+ /**
+ * Executes a specified script fragment
+ * @param Code String of script code
+ */
+ virtual bool executeString(const Common::String &code) = 0;
+
+ /**
+ * Returns a pointer to the main object of the script engine
+ * Note: Using this method breaks the encapsulation of the language from the rest of the engine.
+ */
+ virtual void *getScriptObject() = 0;
+
+ /**
+ * Makes the command line parameters for the script environment available
+ * Note: How the command line parameters will be used by scripts is dependant on the
+ * particular implementation.
+ * @param CommandLineParameters List containing the command line parameters
+ */
+ virtual void setCommandLine(const Common::Array<Common::String> &commandLineParameters) = 0;
+
+ virtual bool persist(OutputPersistenceBlock &writer) = 0;
+ virtual bool unpersist(InputPersistenceBlock &reader) = 0;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp
new file mode 100644
index 0000000000..c753afd9b9
--- /dev/null
+++ b/engines/sword25/sfx/soundengine.cpp
@@ -0,0 +1,274 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#define BS_LOG_PREFIX "SOUNDENGINE"
+
+#include "sword25/sword25.h"
+#include "sword25/sfx/soundengine.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/kernel/resource.h"
+
+#include "sound/decoders/vorbis.h"
+
+namespace Sword25 {
+
+class SoundResource : public Resource {
+public:
+ SoundResource(const Common::String &fileName) : Resource(fileName, Resource::TYPE_SOUND), _fname(fileName) {}
+ virtual ~SoundResource() {
+ debugC(1, kDebugSound, "SoundResource: Unloading file %s", _fname.c_str());
+ }
+
+private:
+ Common::String _fname;
+};
+
+
+SoundEngine::SoundEngine(Kernel *pKernel) : ResourceService(pKernel) {
+ if (!registerScriptBindings())
+ BS_LOG_ERRORLN("Script bindings could not be registered.");
+ else
+ BS_LOGLN("Script bindings registered.");
+
+ _mixer = g_system->getMixer();
+
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ _handles[i].type = kFreeHandle;
+}
+
+Service *SoundEngine_CreateObject(Kernel *pKernel) {
+ return new SoundEngine(pKernel);
+}
+
+bool SoundEngine::init(uint sampleRate, uint channels) {
+ warning("STUB: SoundEngine::init(%d, %d)", sampleRate, channels);
+
+ return true;
+}
+
+void SoundEngine::update() {
+}
+
+void SoundEngine::setVolume(float volume, SOUND_TYPES type) {
+ warning("STUB: SoundEngine::setVolume(%f, %d)", volume, type);
+}
+
+float SoundEngine::getVolume(SOUND_TYPES type) {
+ warning("STUB: SoundEngine::getVolume(%d)", type);
+ return 0;
+}
+
+void SoundEngine::pauseAll() {
+ debugC(1, kDebugSound, "SoundEngine::pauseAll()");
+
+ _mixer->pauseAll(true);
+}
+
+void SoundEngine::resumeAll() {
+ debugC(1, kDebugSound, "SoundEngine::resumeAll()");
+
+ _mixer->pauseAll(false);
+}
+
+void SoundEngine::pauseLayer(uint layer) {
+ warning("STUB: SoundEngine::pauseLayer(%d)", layer);
+}
+
+void SoundEngine::resumeLayer(uint layer) {
+ warning("STUB: SoundEngine::resumeLayer(%d)", layer);
+}
+
+SndHandle *SoundEngine::getHandle(uint *id) {
+
+ // NOTE: Index 0 means error. Thus we're not using it
+ for (uint i = 1; i < SOUND_HANDLES; i++) {
+ if (_handles[i].type != kFreeHandle && !_mixer->isSoundHandleActive(_handles[i].handle)) {
+ debugC(kDebugSound, 5, "Handle %d has finished playing", i);
+ _handles[i].type = kFreeHandle;
+ }
+ }
+
+ for (uint i = 1; i < SOUND_HANDLES; i++) {
+ if (_handles[i].type == kFreeHandle) {
+ debugC(kDebugSound, 5, "Allocated handle %d", i);
+ if (id)
+ *id = i;
+ return &_handles[i];
+ }
+ }
+
+ error("Sound::getHandle(): Too many sound handles");
+
+ return NULL;
+}
+
+Audio::Mixer::SoundType getType(SoundEngine::SOUND_TYPES type) {
+ switch (type) {
+ case SoundEngine::MUSIC:
+ return Audio::Mixer::kMusicSoundType;
+ case SoundEngine::SPEECH:
+ return Audio::Mixer::kSpeechSoundType;
+ case SoundEngine::SFX:
+ return Audio::Mixer::kSFXSoundType;
+ default:
+ error("Unknown SOUND_TYPE");
+ }
+
+ return Audio::Mixer::kPlainSoundType;
+}
+
+bool SoundEngine::playSound(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer) {
+ debugC(1, kDebugSound, "SoundEngine::playSound(%s, %d, %f, %f, %d, %d, %d, %d)", fileName.c_str(), type, volume, pan, loop, loopStart, loopEnd, layer);
+
+ playSoundEx(fileName, type, volume, pan, loop, loopStart, loopEnd, layer);
+
+ return true;
+}
+
+uint SoundEngine::playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume, float pan, bool loop, int loopStart, int loopEnd, uint layer) {
+ Common::SeekableReadStream *in = Kernel::GetInstance()->GetPackage()->getStream(fileName);
+ Audio::SeekableAudioStream *stream = Audio::makeVorbisStream(in, DisposeAfterUse::YES);
+ uint id;
+ SndHandle *handle = getHandle(&id);
+
+ debugC(1, kDebugSound, "SoundEngine::playSoundEx(%s, %d, %f, %f, %d, %d, %d, %d)", fileName.c_str(), type, volume, pan, loop, loopStart, loopEnd, layer);
+
+ _mixer->playStream(getType(type), &(handle->handle), stream, -1, (byte)(volume * 255), (int8)(pan * 127));
+
+ return id;
+}
+
+void SoundEngine::setSoundVolume(uint handle, float volume) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::setSoundVolume(%d, %f)", handle, volume);
+
+ _mixer->setChannelVolume(_handles[handle].handle, (byte)(volume * 255));
+}
+
+void SoundEngine::setSoundPanning(uint handle, float pan) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::setSoundPanning(%d, %f)", handle, pan);
+
+ _mixer->setChannelBalance(_handles[handle].handle, (int8)(pan * 127));
+}
+
+void SoundEngine::pauseSound(uint handle) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::pauseSound(%d)", handle);
+
+ _mixer->pauseHandle(_handles[handle].handle, true);
+}
+
+void SoundEngine::resumeSound(uint handle) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::resumeSound(%d)", handle);
+
+ _mixer->pauseHandle(_handles[handle].handle, false);
+}
+
+void SoundEngine::stopSound(uint handle) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::stopSound(%d)", handle);
+
+ _mixer->stopHandle(_handles[handle].handle);
+}
+
+bool SoundEngine::isSoundPaused(uint handle) {
+ warning("STUB: SoundEngine::isSoundPaused(%d)", handle);
+
+ return false;
+}
+
+bool SoundEngine::isSoundPlaying(uint handle) {
+ assert(handle < SOUND_HANDLES);
+
+ debugC(1, kDebugSound, "SoundEngine::isSoundPlaying(%d)", handle);
+
+ return _mixer->isSoundHandleActive(_handles[handle].handle);
+}
+
+float SoundEngine::getSoundVolume(uint handle) {
+ warning("STUB: SoundEngine::getSoundVolume(%d)", handle);
+
+ return 0;
+}
+
+float SoundEngine::getSoundPanning(uint handle) {
+ warning("STUB: SoundEngine::getSoundPanning(%d)", handle);
+
+ return 0;
+}
+
+float SoundEngine::getSoundTime(uint handle) {
+ warning("STUB: SoundEngine::getSoundTime(%d)", handle);
+
+ return 0;
+}
+
+Resource *SoundEngine::loadResource(const Common::String &fileName) {
+ warning("STUB: SoundEngine::loadResource(%s)", fileName.c_str());
+
+ return new SoundResource(fileName);
+}
+
+bool SoundEngine::canLoadResource(const Common::String &fileName) {
+ Common::String fname = fileName;
+
+ debugC(1, kDebugSound, "SoundEngine::canLoadResource(%s)", fileName.c_str());
+
+ fname.toLowercase();
+
+ return fname.hasSuffix(".ogg");
+}
+
+
+bool SoundEngine::persist(OutputPersistenceBlock &writer) {
+ warning("STUB: SoundEngine::persist()");
+
+ return true;
+}
+
+bool SoundEngine::unpersist(InputPersistenceBlock &reader) {
+ warning("STUB: SoundEngine::unpersist()");
+
+ return true;
+}
+
+
+} // End of namespace Sword25
diff --git a/engines/sword25/sfx/soundengine.h b/engines/sword25/sfx/soundengine.h
new file mode 100644
index 0000000000..81383b12cb
--- /dev/null
+++ b/engines/sword25/sfx/soundengine.h
@@ -0,0 +1,263 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+/*
+ * BS_SoundEngine
+ * -------------
+ * This is the sound engine interface that contains all the methods a sound
+ * engine must implement.
+ * Implementations of the sound engine have to be derived from this class.
+ * It should be noted that a sound engine must maintain a list of all the
+ * samples it uses, and that these samples should be released when the
+ * destructor is called.
+ *
+ * Autor: Malte Thiesen
+ */
+
+#ifndef SWORD25_SOUNDENGINE_H
+#define SWORD25_SOUNDENGINE_H
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/resservice.h"
+#include "sword25/kernel/persistable.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Sword25 {
+
+#define SOUND_HANDLES 32
+
+enum sndHandleType {
+ kFreeHandle,
+ kEffectHandle,
+ kVoiceHandle
+};
+
+struct SndHandle {
+ Audio::SoundHandle handle;
+ sndHandleType type;
+};
+
+
+class SoundEngine : public ResourceService, public Persistable {
+public:
+ enum SOUND_TYPES {
+ MUSIC = 0,
+ SPEECH = 1,
+ SFX = 2
+ };
+
+ /**
+ * The callback function of PlayDynamicSoundEx
+ * @param UserData User-specified pointer
+ * @param Data Pointer to the data buffer
+ * @param DataLength Length of the data to be written in bytes
+ */
+ typedef void (*DynamicSoundReadCallback)(void *UserData, void *Data, uint DataLength);
+
+ SoundEngine(Kernel *pKernel);
+ ~SoundEngine() {};
+
+ /**
+ * Initialises the sound engine
+ * @param SampleRate Specifies the sample rate to use.
+ * @param Channels The maximum number of channels. The default is 32.
+ * @return Returns true on success, otherwise false.
+ * @remark Calls to other methods may take place only if this
+ * method was called successfully.
+ */
+ bool init(uint sampleRate, uint channels = 32);
+
+ /**
+ * Performs a "tick" of the sound engine
+ *
+ * This method should be called once per frame. It can be used by implementations
+ * of the sound engine that are not running in their own thread, or to perform
+ * additional administrative tasks that are needed.
+ */
+ void update();
+
+ /**
+ * Sets the default volume for the different sound types
+ * @param Volume The default volume level (0 = off, 1 = full volume)
+ * @param Type The SoundType whose volume is to be changed
+ */
+ void setVolume(float volume, SOUND_TYPES type);
+
+ /**
+ * Specifies the default volume of different sound types
+ * @param Type The SoundType
+ * @return Returns the standard sound volume for the given type
+ * (0 = off, 1 = full volume).
+ */
+ float getVolume(SOUND_TYPES type);
+
+ /**
+ * Pauses all the sounds that are playing.
+ */
+ void pauseAll();
+
+ /**
+ * Resumes all currently stopped sounds
+ */
+ void resumeAll();
+
+ /**
+ * Pauses all sounds of a given layer.
+ * @param Layer The Sound Layer
+ */
+ void pauseLayer(uint layer);
+
+ /**
+ * Resumes all the sounds in a layer that was previously stopped with PauseLayer()
+ * @param Layer The Sound Layer
+ */
+ void resumeLayer(uint layer);
+
+ /**
+ * Plays a sound
+ * @param FileName The filename of the sound to be played
+ * @param Type The type of sound
+ * @param Volume The volume of the sound (0 = off, 1 = full volume)
+ * @param Pan Panning (-1 = full left, 1 = right)
+ * @param Loop Indicates whether the sound should be looped
+ * @param LoopStart Indicates the starting loop point. If a value less than 0 is passed, the start
+ * of the sound is used.
+ * @param LoopEnd Indicates the ending loop point. If a avlue is passed less than 0, the end of
+ * the sound is used.
+ * @param Layer The sound layer
+ * @return Returns true if the playback of the sound was started successfully.
+ * @remark If more control is needed over the playing, eg. changing the sound parameters
+ * for Volume and Panning, then PlaySoundEx should be used.
+ */
+ bool playSound(const Common::String &fileName, SOUND_TYPES type, float volume = 1.0f, float pan = 0.0f, bool loop = false, int loopStart = -1, int loopEnd = -1, uint layer = 0);
+
+ /**
+ * Plays a sound
+ * @param Type The type of sound
+ * @param Volume The volume of the sound (0 = off, 1 = full volume)
+ * @param Pan Panning (-1 = full left, 1 = right)
+ * @param Loop Indicates whether the sound should be looped
+ * @param LoopStart Indicates the starting loop point. If a value less than 0 is passed, the start
+ * of the sound is used.
+ * @param LoopEnd Indicates the ending loop point. If a avlue is passed less than 0, the end of
+ * the sound is used.
+ * @param Layer The sound layer
+ * @return Returns a handle to the sound. With this handle, the sound can be manipulated during playback.
+ * @remark If more control is needed over the playing, eg. changing the sound parameters
+ * for Volume and Panning, then PlaySoundEx should be used.
+ */
+ uint playSoundEx(const Common::String &fileName, SOUND_TYPES type, float volume = 1.0f, float pan = 0.0f, bool loop = false, int loopStart = -1, int loopEnd = -1, uint layer = 0);
+
+ /**
+ * Sets the volume of a playing sound
+ * @param Handle The sound handle
+ * @param Volume The volume of the sound (0 = off, 1 = full volume)
+ */
+ void setSoundVolume(uint handle, float volume);
+
+ /**
+ * Sets the panning of a playing sound
+ * @param Handle The sound handle
+ * @param Pan Panning (-1 = full left, 1 = right)
+ */
+ void setSoundPanning(uint handle, float pan);
+
+ /**
+ * Pauses a playing sound
+ * @param Handle The sound handle
+ */
+ void pauseSound(uint handle);
+
+ /**
+ * Resumes a paused sound
+ * @param Handle The sound handle
+ */
+ void resumeSound(uint handle);
+
+ /**
+ * Stops a playing sound
+ * @param Handle The sound handle
+ * @remark Calling this method invalidates the passed handle; it can no longer be used.
+ */
+ void stopSound(uint handle);
+
+ /**
+ * Returns whether a sound is paused
+ * @param Handle The sound handle
+ * @return Returns true if the sound is paused, false otherwise.
+ */
+ bool isSoundPaused(uint handle);
+
+ /**
+ * Returns whether a sound is still playing.
+ * @param Handle The sound handle
+ * @return Returns true if the sound is playing, false otherwise.
+ */
+ bool isSoundPlaying(uint handle);
+
+ /**
+ * Returns the volume of a playing sound (0 = off, 1 = full volume)
+ */
+ float getSoundVolume(uint handle);
+
+ /**
+ * Returns the panning of a playing sound (-1 = full left, 1 = right)
+ */
+ float getSoundPanning(uint handle);
+
+ /**
+ * Returns the position within a playing sound in seconds
+ */
+ float getSoundTime(uint handle);
+
+ Resource *loadResource(const Common::String &fileName);
+ bool canLoadResource(const Common::String &fileName);
+
+ bool persist(OutputPersistenceBlock &writer);
+ bool unpersist(InputPersistenceBlock &reader);
+
+private:
+ bool registerScriptBindings();
+ SndHandle *getHandle(uint *id);
+
+private:
+ Audio::Mixer *_mixer;
+ SndHandle _handles[SOUND_HANDLES];
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/sfx/soundengine_script.cpp b/engines/sword25/sfx/soundengine_script.cpp
new file mode 100644
index 0000000000..2808296799
--- /dev/null
+++ b/engines/sword25/sfx/soundengine_script.cpp
@@ -0,0 +1,365 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "sword25/kernel/common.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/script/script.h"
+#include "sword25/script/luabindhelper.h"
+
+#include "sword25/sfx/soundengine.h"
+
+namespace Sword25 {
+
+static int init(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ if (lua_gettop(L) == 0)
+ lua_pushbooleancpp(L, pSfx->init(44100, 32));
+ else if (lua_gettop(L) == 1)
+ lua_pushbooleancpp(L, pSfx->init(static_cast<uint>(luaL_checknumber(L, 1)), 32));
+ else
+ lua_pushbooleancpp(L, pSfx->init(static_cast<uint>(luaL_checknumber(L, 1)), static_cast<uint>(luaL_checknumber(L, 2))));
+
+ return 1;
+}
+
+static int update(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->update();
+
+ return 0;
+}
+
+static int setVolume(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->setVolume(static_cast<float>(luaL_checknumber(L, 1)),
+ static_cast<SoundEngine::SOUND_TYPES>(static_cast<uint>(luaL_checknumber(L, 2))));
+
+ return 0;
+}
+
+static int getVolume(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->getVolume(static_cast<SoundEngine::SOUND_TYPES>(static_cast<uint>(luaL_checknumber(L, 1)))));
+
+ return 1;
+}
+
+static int pauseAll(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->pauseAll();
+
+ return 0;
+}
+
+static int resumeAll(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->resumeAll();
+
+ return 0;
+}
+
+static int pauseLayer(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->pauseLayer(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int resumeLayer(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->resumeLayer(static_cast<int>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static void processPlayParams(lua_State *L, Common::String &fileName, SoundEngine::SOUND_TYPES &type, float &volume, float &pan, bool &loop, int &loopStart, int &loopEnd, uint &layer) {
+ fileName = luaL_checkstring(L, 1);
+
+ type = static_cast<SoundEngine::SOUND_TYPES>(static_cast<uint>(luaL_checknumber(L, 2)));
+
+ if (lua_gettop(L) < 3 || lua_isnil(L, 3))
+ volume = 1.0f;
+ else
+ volume = static_cast<float>(luaL_checknumber(L, 3));
+
+ if (lua_gettop(L) < 4 || lua_isnil(L, 4))
+ pan = 0.0f;
+ else
+ pan = static_cast<float>(luaL_checknumber(L, 4));
+
+ if (lua_gettop(L) < 5 || lua_isnil(L, 5))
+ loop = false;
+ else
+ loop = lua_tobooleancpp(L, 5);
+
+ if (lua_gettop(L) < 6 || lua_isnil(L, 6))
+ loopStart = -1;
+ else
+ loopStart = static_cast<int>(luaL_checknumber(L, 6));
+
+ if (lua_gettop(L) < 7 || lua_isnil(L, 7))
+ loopEnd = -1;
+ else
+ loopEnd = static_cast<int>(luaL_checknumber(L, 7));
+
+ if (lua_gettop(L) < 8 || lua_isnil(L, 8))
+ layer = 0;
+ else
+ layer = static_cast<uint>(luaL_checknumber(L, 8));
+}
+
+static int playSound(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ Common::String fileName;
+ SoundEngine::SOUND_TYPES type;
+ float volume;
+ float pan;
+ bool loop;
+ int loopStart;
+ int loopEnd;
+ uint layer;
+ processPlayParams(L, fileName, type, volume, pan, loop, loopStart, loopEnd, layer);
+
+ lua_pushbooleancpp(L, pSfx->playSound(fileName, type, volume, pan, loop, loopStart, loopEnd, layer));
+
+ return 1;
+}
+
+static int playSoundEx(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ Common::String fileName;
+ SoundEngine::SOUND_TYPES type;
+ float volume;
+ float pan;
+ bool loop;
+ int loopStart;
+ int loopEnd;
+ uint layer;
+ processPlayParams(L, fileName, type, volume, pan, loop, loopStart, loopEnd, layer);
+
+ lua_pushnumber(L, pSfx->playSoundEx(fileName, type, volume, pan, loop, loopStart, loopEnd, layer));
+
+ return 1;
+}
+
+static int setSoundVolume(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->setSoundVolume(static_cast<uint>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+static int setSoundPanning(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->setSoundPanning(static_cast<uint>(luaL_checknumber(L, 1)), static_cast<float>(luaL_checknumber(L, 2)));
+
+ return 0;
+}
+
+static int pauseSound(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->pauseSound(static_cast<uint>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int resumeSound(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->resumeSound(static_cast<uint>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int stopSound(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ pSfx->stopSound(static_cast<uint>(luaL_checknumber(L, 1)));
+
+ return 0;
+}
+
+static int isSoundPaused(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushbooleancpp(L, pSfx->isSoundPaused(static_cast<uint>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+static int isSoundPlaying(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushbooleancpp(L, pSfx->isSoundPlaying(static_cast<uint>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+static int getSoundVolume(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->getSoundVolume(static_cast<uint>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+static int getSoundPanning(lua_State *L) {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ SoundEngine *pSfx = static_cast<SoundEngine *>(Kernel::GetInstance()->GetService("sfx"));
+ BS_ASSERT(pSfx);
+
+ lua_pushnumber(L, pSfx->getSoundPanning(static_cast<uint>(luaL_checknumber(L, 1))));
+
+ return 1;
+}
+
+static const char *SFX_LIBRARY_NAME = "Sfx";
+
+static const luaL_reg SFX_FUNCTIONS[] = {
+ {"Init", init},
+ {"Update", update},
+ {"__SetVolume", setVolume},
+ {"__GetVolume", getVolume},
+ {"PauseAll", pauseAll},
+ {"ResumeAll", resumeAll},
+ {"PauseLayer", pauseLayer},
+ {"ResumeLayer", resumeLayer},
+ {"__PlaySound", playSound},
+ {"__PlaySoundEx", playSoundEx},
+ {"__SetSoundVolume", setSoundVolume},
+ {"__SetSoundPanning", setSoundPanning},
+ {"__PauseSound", pauseSound},
+ {"__ResumeSound", resumeSound},
+ {"__StopSound", stopSound},
+ {"__IsSoundPaused", isSoundPaused},
+ {"__IsSoundPlaying", isSoundPlaying},
+ {"__GetSoundVolume", getSoundVolume},
+ {"__GetSoundPanning", getSoundPanning},
+ {0, 0}
+};
+
+static const lua_constant_reg SFX_CONSTANTS[] = {
+ {"MUSIC", SoundEngine::MUSIC},
+ {"SPEECH", SoundEngine::SPEECH},
+ {"SFX", SoundEngine::SFX},
+ {0, 0}
+};
+
+bool SoundEngine::registerScriptBindings() {
+ Kernel *pKernel = Kernel::GetInstance();
+ BS_ASSERT(pKernel);
+ ScriptEngine *pScript = static_cast<ScriptEngine *>(pKernel->GetService("script"));
+ BS_ASSERT(pScript);
+ lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
+ BS_ASSERT(L);
+
+ if (!LuaBindhelper::addFunctionsToLib(L, SFX_LIBRARY_NAME, SFX_FUNCTIONS)) return false;
+ if (!LuaBindhelper::addConstantsToLib(L, SFX_LIBRARY_NAME, SFX_CONSTANTS)) return false;
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/sword25.cpp b/engines/sword25/sword25.cpp
new file mode 100644
index 0000000000..4e99ade25d
--- /dev/null
+++ b/engines/sword25/sword25.cpp
@@ -0,0 +1,189 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "engines/util.h"
+
+#include "sword25/sword25.h"
+#include "sword25/kernel/filesystemutil.h"
+#include "sword25/kernel/kernel.h"
+#include "sword25/package/packagemanager.h"
+#include "sword25/script/script.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "MAIN"
+
+const char *const PACKAGE_MANAGER = "archiveFS";
+const char *const DEFAULT_SCRIPT_FILE = "/system/boot.lua";
+
+void logToStdout(const char *Message) {
+ debugN(0, "%s", Message);
+}
+
+Sword25Engine::Sword25Engine(OSystem *syst, const ADGameDescription *gameDesc):
+ Engine(syst),
+ _gameDescription(gameDesc) {
+
+ DebugMan.addDebugChannel(kDebugScript, "Script", "Script debug level");
+ DebugMan.addDebugChannel(kDebugScript, "Scripts", "Script debug level");
+ DebugMan.addDebugChannel(kDebugSound, "Sound", "Sound debug level");
+}
+
+Sword25Engine::~Sword25Engine() {
+}
+
+Common::Error Sword25Engine::run() {
+ // Engine initialisation
+ Common::Error errorCode = appStart();
+ if (errorCode != Common::kNoError) {
+ appEnd();
+ return errorCode;
+ }
+
+ // Run the game
+ bool runSuccess = appMain();
+
+ // Engine de-initialisation
+ bool deinitSuccess = appEnd();
+
+ return (runSuccess && deinitSuccess) ? Common::kNoError : Common::kUnknownError;
+}
+
+Common::Error Sword25Engine::appStart() {
+ // All log messages will be sent to StdOut
+ BS_Log::RegisterLogListener(logToStdout);
+
+ // Initialise the graphics mode to ARGB8888
+ Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
+ initGraphics(800, 600, true, &format);
+ if (format != g_system->getScreenFormat())
+ return Common::kUnsupportedColorMode;
+
+ // Kernel initialization
+ if (!Kernel::GetInstance()->GetInitSuccess()) {
+ BS_LOG_ERRORLN("Kernel initialization failed.");
+ return Common::kUnknownError;
+ }
+
+ // Package-Manager starten, damit die Packfiles geladen werden können.
+ PackageManager *packageManagerPtr = static_cast<PackageManager *>(Kernel::GetInstance()->NewService("package", PACKAGE_MANAGER));
+ if (!packageManagerPtr) {
+ BS_LOG_ERRORLN("PackageManager initialization failed.");
+ return Common::kUnknownError;
+ }
+
+ // Packages laden oder das aktuelle Verzeichnis mounten, wenn das über Kommandozeile angefordert wurde.
+ if (getGameFlags() & GF_EXTRACTED) {
+ if (!packageManagerPtr->loadDirectoryAsPackage(ConfMan.get("path"), "/"))
+ return Common::kUnknownError;
+ } else {
+ if (!loadPackages())
+ return Common::kUnknownError;
+ }
+
+ // Einen Pointer auf den Skript-Engine holen.
+ ScriptEngine *scriptPtr = static_cast<ScriptEngine *>(Kernel::GetInstance()->GetService("script"));
+ if (!scriptPtr) {
+ BS_LOG_ERRORLN("Script intialization failed.");
+ return Common::kUnknownError;
+ }
+
+ Common::StringArray commandParameters;
+ scriptPtr->setCommandLine(commandParameters);
+
+ return Common::kNoError;
+}
+
+bool Sword25Engine::appMain() {
+ // The main script start. This script loads all the other scripts and starts the actual game.
+ ScriptEngine *scriptPtr = static_cast<ScriptEngine *>(Kernel::GetInstance()->GetService("script"));
+ BS_ASSERT(scriptPtr);
+ scriptPtr->executeFile(DEFAULT_SCRIPT_FILE);
+
+ return true;
+}
+
+bool Sword25Engine::appEnd() {
+ // The kernel is shutdown, and un-initialises all subsystems
+ Kernel::DeleteInstance();
+
+ // Free the log file if it was used
+ BS_Log::_CloseLog();
+
+ return true;
+}
+
+bool Sword25Engine::loadPackages() {
+ PackageManager *packageManagerPtr = reinterpret_cast<PackageManager *>(Kernel::GetInstance()->GetService("package"));
+ BS_ASSERT(packageManagerPtr);
+
+ // Load the main package
+ if (!packageManagerPtr->loadPackage("data.b25c", "/")) return false;
+
+ // Get the contents of the main program directory and sort them alphabetically
+ Common::FSNode dir(ConfMan.get("path"));
+ Common::FSList files;
+ if (!dir.isDirectory() || !dir.getChildren(files, Common::FSNode::kListAll)) {
+ warning("Game data path does not exist or is not a directory");
+ return false;
+ }
+
+ Common::sort(files.begin(), files.end());
+
+ // Identity all patch packages
+ // The filename of patch packages must have the form patch??.b25c, with the question marks
+ // are placeholders for numbers.
+ // Since the filenames have been sorted, patches are mounted with low numbers first, through
+ // to ones with high numbers. This is important, because newly mount packages overwrite
+ // existing files in the virtual file system, if they include files with the same name.
+ for (Common::FSList::const_iterator it = files.begin(); it != files.end(); ++it) {
+ if (it->getName().matchString("patch???.b25c", true))
+ if (!packageManagerPtr->loadPackage(it->getName(), "/"))
+ return false;
+ }
+
+ // Identity and mount all language packages
+ // The filename of the packages have the form lang_*.b25c (eg. lang_de.b25c)
+ for (Common::FSList::const_iterator it = files.begin(); it != files.end(); ++it) {
+ if (it->getName().matchString("lang_*.b25c", true))
+ if (!packageManagerPtr->loadPackage(it->getName(), "/"))
+ return false;
+ }
+
+ return true;
+}
+
+} // End of namespace Sword25
diff --git a/engines/sword25/sword25.h b/engines/sword25/sword25.h
new file mode 100644
index 0000000000..8f31a05562
--- /dev/null
+++ b/engines/sword25/sword25.h
@@ -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$
+ *
+ */
+
+#ifndef SWORD25_H
+#define SWORD25_H
+
+#include "common/scummsys.h"
+#include "common/str-array.h"
+#include "common/util.h"
+#include "engines/engine.h"
+
+#include "sword25/kernel/log.h"
+
+struct ADGameDescription;
+
+namespace Sword25 {
+
+enum {
+ kFileTypeHash = 0
+};
+
+enum {
+ kDebugScript = 1 << 0,
+ kDebugSound = 1 << 1
+};
+
+enum GameFlags {
+ GF_EXTRACTED = 1 << 0
+};
+
+#define MESSAGE_BASIC 1
+#define MESSAGE_INTERMEDIATE 2
+#define MESSAGE_DETAILED 3
+
+class Sword25Engine : public Engine {
+private:
+ Common::Error appStart();
+ bool appMain();
+ bool appEnd();
+
+ bool loadPackages();
+
+protected:
+ virtual Common::Error run();
+ void shutdown();
+
+public:
+ Sword25Engine(OSystem *syst, const ADGameDescription *gameDesc);
+ virtual ~Sword25Engine();
+
+ uint32 getGameFlags() const;
+
+ const ADGameDescription *_gameDescription;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/util/lua/COPYRIGHT b/engines/sword25/util/lua/COPYRIGHT
new file mode 100644
index 0000000000..3a53e741e0
--- /dev/null
+++ b/engines/sword25/util/lua/COPYRIGHT
@@ -0,0 +1,34 @@
+Lua License
+-----------
+
+Lua is licensed under the terms of the MIT license reproduced below.
+This means that Lua is free software and can be used for both academic
+and commercial purposes at absolutely no cost.
+
+For details and rationale, see http://www.lua.org/license.html .
+
+===============================================================================
+
+Copyright (C) 1994-2008 Lua.org, PUC-Rio.
+
+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, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+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. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+
+(end of COPYRIGHT)
diff --git a/engines/sword25/util/lua/HISTORY b/engines/sword25/util/lua/HISTORY
new file mode 100644
index 0000000000..ce0c95bc69
--- /dev/null
+++ b/engines/sword25/util/lua/HISTORY
@@ -0,0 +1,183 @@
+HISTORY for Lua 5.1
+
+* Changes from version 5.0 to 5.1
+ -------------------------------
+ Language:
+ + new module system.
+ + new semantics for control variables of fors.
+ + new semantics for setn/getn.
+ + new syntax/semantics for varargs.
+ + new long strings and comments.
+ + new `mod' operator (`%')
+ + new length operator #t
+ + metatables for all types
+ API:
+ + new functions: lua_createtable, lua_get(set)field, lua_push(to)integer.
+ + user supplies memory allocator (lua_open becomes lua_newstate).
+ + luaopen_* functions must be called through Lua.
+ Implementation:
+ + new configuration scheme via luaconf.h.
+ + incremental garbage collection.
+ + better handling of end-of-line in the lexer.
+ + fully reentrant parser (new Lua function `load')
+ + better support for 64-bit machines.
+ + native loadlib support for Mac OS X.
+ + standard distribution in only one library (lualib.a merged into lua.a)
+
+* Changes from version 4.0 to 5.0
+ -------------------------------
+ Language:
+ + lexical scoping.
+ + Lua coroutines.
+ + standard libraries now packaged in tables.
+ + tags replaced by metatables and tag methods replaced by metamethods,
+ stored in metatables.
+ + proper tail calls.
+ + each function can have its own global table, which can be shared.
+ + new __newindex metamethod, called when we insert a new key into a table.
+ + new block comments: --[[ ... ]].
+ + new generic for.
+ + new weak tables.
+ + new boolean type.
+ + new syntax "local function".
+ + (f()) returns the first value returned by f.
+ + {f()} fills a table with all values returned by f.
+ + \n ignored in [[\n .
+ + fixed and-or priorities.
+ + more general syntax for function definition (e.g. function a.x.y:f()...end).
+ + more general syntax for function calls (e.g. (print or write)(9)).
+ + new functions (time/date, tmpfile, unpack, require, load*, etc.).
+ API:
+ + chunks are loaded by using lua_load; new luaL_loadfile and luaL_loadbuffer.
+ + introduced lightweight userdata, a simple "void*" without a metatable.
+ + new error handling protocol: the core no longer prints error messages;
+ all errors are reported to the caller on the stack.
+ + new lua_atpanic for host cleanup.
+ + new, signal-safe, hook scheme.
+ Implementation:
+ + new license: MIT.
+ + new, faster, register-based virtual machine.
+ + support for external multithreading and coroutines.
+ + new and consistent error message format.
+ + the core no longer needs "stdio.h" for anything (except for a single
+ use of sprintf to convert numbers to strings).
+ + lua.c now runs the environment variable LUA_INIT, if present. It can
+ be "@filename", to run a file, or the chunk itself.
+ + support for user extensions in lua.c.
+ sample implementation given for command line editing.
+ + new dynamic loading library, active by default on several platforms.
+ + safe garbage-collector metamethods.
+ + precompiled bytecodes checked for integrity (secure binary dostring).
+ + strings are fully aligned.
+ + position capture in string.find.
+ + read('*l') can read lines with embedded zeros.
+
+* Changes from version 3.2 to 4.0
+ -------------------------------
+ Language:
+ + new "break" and "for" statements (both numerical and for tables).
+ + uniform treatment of globals: globals are now stored in a Lua table.
+ + improved error messages.
+ + no more '$debug': full speed *and* full debug information.
+ + new read form: read(N) for next N bytes.
+ + general read patterns now deprecated.
+ (still available with -DCOMPAT_READPATTERNS.)
+ + all return values are passed as arguments for the last function
+ (old semantics still available with -DLUA_COMPAT_ARGRET)
+ + garbage collection tag methods for tables now deprecated.
+ + there is now only one tag method for order.
+ API:
+ + New API: fully re-entrant, simpler, and more efficient.
+ + New debug API.
+ Implementation:
+ + faster than ever: cleaner virtual machine and new hashing algorithm.
+ + non-recursive garbage-collector algorithm.
+ + reduced memory usage for programs with many strings.
+ + improved treatment for memory allocation errors.
+ + improved support for 16-bit machines (we hope).
+ + code now compiles unmodified as both ANSI C and C++.
+ + numbers in bases other than 10 are converted using strtoul.
+ + new -f option in Lua to support #! scripts.
+ + luac can now combine text and binaries.
+
+* Changes from version 3.1 to 3.2
+ -------------------------------
+ + redirected all output in Lua's core to _ERRORMESSAGE and _ALERT.
+ + increased limit on the number of constants and globals per function
+ (from 2^16 to 2^24).
+ + debugging info (lua_debug and hooks) moved into lua_state and new API
+ functions provided to get and set this info.
+ + new debug lib gives full debugging access within Lua.
+ + new table functions "foreachi", "sort", "tinsert", "tremove", "getn".
+ + new io functions "flush", "seek".
+
+* Changes from version 3.0 to 3.1
+ -------------------------------
+ + NEW FEATURE: anonymous functions with closures (via "upvalues").
+ + new syntax:
+ - local variables in chunks.
+ - better scope control with DO block END.
+ - constructors can now be also written: { record-part; list-part }.
+ - more general syntax for function calls and lvalues, e.g.:
+ f(x).y=1
+ o:f(x,y):g(z)
+ f"string" is sugar for f("string")
+ + strings may now contain arbitrary binary data (e.g., embedded zeros).
+ + major code re-organization and clean-up; reduced module interdependecies.
+ + no arbitrary limits on the total number of constants and globals.
+ + support for multiple global contexts.
+ + better syntax error messages.
+ + new traversal functions "foreach" and "foreachvar".
+ + the default for numbers is now double.
+ changing it to use floats or longs is easy.
+ + complete debug information stored in pre-compiled chunks.
+ + sample interpreter now prompts user when run interactively, and also
+ handles control-C interruptions gracefully.
+
+* Changes from version 2.5 to 3.0
+ -------------------------------
+ + NEW CONCEPT: "tag methods".
+ Tag methods replace fallbacks as the meta-mechanism for extending the
+ semantics of Lua. Whereas fallbacks had a global nature, tag methods
+ work on objects having the same tag (e.g., groups of tables).
+ Existing code that uses fallbacks should work without change.
+ + new, general syntax for constructors {[exp] = exp, ... }.
+ + support for handling variable number of arguments in functions (varargs).
+ + support for conditional compilation ($if ... $else ... $end).
+ + cleaner semantics in API simplifies host code.
+ + better support for writing libraries (auxlib.h).
+ + better type checking and error messages in the standard library.
+ + luac can now also undump.
+
+* Changes from version 2.4 to 2.5
+ -------------------------------
+ + io and string libraries are now based on pattern matching;
+ the old libraries are still available for compatibility
+ + dofile and dostring can now return values (via return statement)
+ + better support for 16- and 64-bit machines
+ + expanded documentation, with more examples
+
+* Changes from version 2.2 to 2.4
+ -------------------------------
+ + external compiler creates portable binary files that can be loaded faster
+ + interface for debugging and profiling
+ + new "getglobal" fallback
+ + new functions for handling references to Lua objects
+ + new functions in standard lib
+ + only one copy of each string is stored
+ + expanded documentation, with more examples
+
+* Changes from version 2.1 to 2.2
+ -------------------------------
+ + functions now may be declared with any "lvalue" as a name
+ + garbage collection of functions
+ + support for pipes
+
+* Changes from version 1.1 to 2.1
+ -------------------------------
+ + object-oriented support
+ + fallbacks
+ + simplified syntax for tables
+ + many internal improvements
+
+(end of HISTORY)
diff --git a/engines/sword25/util/lua/README b/engines/sword25/util/lua/README
new file mode 100644
index 0000000000..11b4dff70e
--- /dev/null
+++ b/engines/sword25/util/lua/README
@@ -0,0 +1,37 @@
+README for Lua 5.1
+
+See INSTALL for installation instructions.
+See HISTORY for a summary of changes since the last released version.
+
+* What is Lua?
+ ------------
+ Lua is a powerful, light-weight programming language designed for extending
+ applications. Lua is also frequently used as a general-purpose, stand-alone
+ language. Lua is free software.
+
+ For complete information, visit Lua's web site at http://www.lua.org/ .
+ For an executive summary, see http://www.lua.org/about.html .
+
+ Lua has been used in many different projects around the world.
+ For a short list, see http://www.lua.org/uses.html .
+
+* Availability
+ ------------
+ Lua is freely available for both academic and commercial purposes.
+ See COPYRIGHT and http://www.lua.org/license.html for details.
+ Lua can be downloaded at http://www.lua.org/download.html .
+
+* Installation
+ ------------
+ Lua is implemented in pure ANSI C, and compiles unmodified in all known
+ platforms that have an ANSI C compiler. In most Unix-like platforms, simply
+ do "make" with a suitable target. See INSTALL for detailed instructions.
+
+* Origin
+ ------
+ Lua is developed at Lua.org, a laboratory of the Department of Computer
+ Science of PUC-Rio (the Pontifical Catholic University of Rio de Janeiro
+ in Brazil).
+ For more information about the authors, see http://www.lua.org/authors.html .
+
+(end of README)
diff --git a/engines/sword25/util/lua/lapi.c b/engines/sword25/util/lua/lapi.c
new file mode 100644
index 0000000000..d7e8931e45
--- /dev/null
+++ b/engines/sword25/util/lua/lapi.c
@@ -0,0 +1,1085 @@
+/*
+** $Id: lapi.c,v 2.55.1.3 2008/01/03 15:20:39 roberto Exp $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define lapi_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+
+
+
+const char lua_ident[] =
+ "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n"
+ "$Authors: " LUA_AUTHORS " $\n"
+ "$URL: www.lua.org $\n";
+
+
+
+#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base))
+
+#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject)
+
+#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;}
+
+
+
+static TValue *index2adr (lua_State *L, int idx) {
+ if (idx > 0) {
+ TValue *o = L->base + (idx - 1);
+ api_check(L, idx <= L->ci->top - L->base);
+ if (o >= L->top) return cast(TValue *, luaO_nilobject);
+ else return o;
+ }
+ else if (idx > LUA_REGISTRYINDEX) {
+ api_check(L, idx != 0 && -idx <= L->top - L->base);
+ return L->top + idx;
+ }
+ else switch (idx) { /* pseudo-indices */
+ case LUA_REGISTRYINDEX: return registry(L);
+ case LUA_ENVIRONINDEX: {
+ Closure *func = curr_func(L);
+ sethvalue(L, &L->env, func->c.env);
+ return &L->env;
+ }
+ case LUA_GLOBALSINDEX: return gt(L);
+ default: {
+ Closure *func = curr_func(L);
+ idx = LUA_GLOBALSINDEX - idx;
+ return (idx <= func->c.nupvalues)
+ ? &func->c.upvalue[idx-1]
+ : cast(TValue *, luaO_nilobject);
+ }
+ }
+}
+
+
+static Table *getcurrenv (lua_State *L) {
+ if (L->ci == L->base_ci) /* no enclosing function? */
+ return hvalue(gt(L)); /* use global table as environment */
+ else {
+ Closure *func = curr_func(L);
+ return func->c.env;
+ }
+}
+
+
+void luaA_pushobject (lua_State *L, const TValue *o) {
+ setobj2s(L, L->top, o);
+ api_incr_top(L);
+}
+
+
+LUA_API int lua_checkstack (lua_State *L, int size) {
+ int res;
+ lua_lock(L);
+ if ((L->top - L->base + size) > LUAI_MAXCSTACK)
+ res = 0; /* stack overflow */
+ else {
+ luaD_checkstack(L, size);
+ if (L->ci->top < L->top + size)
+ L->ci->top = L->top + size;
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
+ int i;
+ if (from == to) return;
+ lua_lock(to);
+ api_checknelems(from, n);
+ api_check(from, G(from) == G(to));
+ api_check(from, to->ci->top - to->top >= n);
+ from->top -= n;
+ for (i = 0; i < n; i++) {
+ setobj2s(to, to->top++, from->top + i);
+ }
+ lua_unlock(to);
+}
+
+
+LUA_API void lua_setlevel (lua_State *from, lua_State *to) {
+ to->nCcalls = from->nCcalls;
+}
+
+
+LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
+ lua_CFunction old;
+ lua_lock(L);
+ old = G(L)->panic;
+ G(L)->panic = panicf;
+ lua_unlock(L);
+ return old;
+}
+
+
+LUA_API lua_State *lua_newthread (lua_State *L) {
+ lua_State *L1;
+ lua_lock(L);
+ luaC_checkGC(L);
+ L1 = luaE_newthread(L);
+ setthvalue(L, L->top, L1);
+ api_incr_top(L);
+ lua_unlock(L);
+ luai_userstatethread(L, L1);
+ return L1;
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return cast_int(L->top - L->base);
+}
+
+
+LUA_API void lua_settop (lua_State *L, int idx) {
+ lua_lock(L);
+ if (idx >= 0) {
+ api_check(L, idx <= L->stack_last - L->base);
+ while (L->top < L->base + idx)
+ setnilvalue(L->top++);
+ L->top = L->base + idx;
+ }
+ else {
+ api_check(L, -(idx+1) <= (L->top - L->base));
+ L->top += idx+1; /* `subtract' index (index is negative) */
+ }
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_remove (lua_State *L, int idx) {
+ StkId p;
+ lua_lock(L);
+ p = index2adr(L, idx);
+ api_checkvalidindex(L, p);
+ while (++p < L->top) setobjs2s(L, p-1, p);
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_insert (lua_State *L, int idx) {
+ StkId p;
+ StkId q;
+ lua_lock(L);
+ p = index2adr(L, idx);
+ api_checkvalidindex(L, p);
+ for (q = L->top; q>p; q--) setobjs2s(L, q, q-1);
+ setobjs2s(L, p, L->top);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_replace (lua_State *L, int idx) {
+ StkId o;
+ lua_lock(L);
+ /* explicit test for incompatible code */
+ if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)
+ luaG_runerror(L, "no calling environment");
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ if (idx == LUA_ENVIRONINDEX) {
+ Closure *func = curr_func(L);
+ api_check(L, ttistable(L->top - 1));
+ func->c.env = hvalue(L->top - 1);
+ luaC_barrier(L, func, L->top - 1);
+ }
+ else {
+ setobj(L, o, L->top - 1);
+ if (idx < LUA_GLOBALSINDEX) /* function upvalue? */
+ luaC_barrier(L, curr_func(L), L->top - 1);
+ }
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int idx) {
+ lua_lock(L);
+ setobj2s(L, L->top, index2adr(L, idx));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);
+}
+
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ return (t == LUA_TNONE) ? "no value" : luaT_typenames[t];
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return iscfunction(o);
+}
+
+
+LUA_API int lua_isnumber (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ return tonumber(o, &n);
+}
+
+
+LUA_API int lua_isstring (lua_State *L, int idx) {
+ int t = lua_type(L, idx);
+ return (t == LUA_TSTRING || t == LUA_TNUMBER);
+}
+
+
+LUA_API int lua_isuserdata (lua_State *L, int idx) {
+ const TValue *o = index2adr(L, idx);
+ return (ttisuserdata(o) || ttislightuserdata(o));
+}
+
+
+LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
+ StkId o1 = index2adr(L, index1);
+ StkId o2 = index2adr(L, index2);
+ return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
+ : luaO_rawequalObj(o1, o2);
+}
+
+
+LUA_API int lua_equal (lua_State *L, int index1, int index2) {
+ StkId o1, o2;
+ int i;
+ lua_lock(L); /* may call tag method */
+ o1 = index2adr(L, index1);
+ o2 = index2adr(L, index2);
+ i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2);
+ lua_unlock(L);
+ return i;
+}
+
+
+LUA_API int lua_lessthan (lua_State *L, int index1, int index2) {
+ StkId o1, o2;
+ int i;
+ lua_lock(L); /* may call tag method */
+ o1 = index2adr(L, index1);
+ o2 = index2adr(L, index2);
+ i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
+ : luaV_lessthan(L, o1, o2);
+ lua_unlock(L);
+ return i;
+}
+
+
+
+LUA_API lua_Number lua_tonumber (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ if (tonumber(o, &n))
+ return nvalue(o);
+ else
+ return 0;
+}
+
+
+LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {
+ TValue n;
+ const TValue *o = index2adr(L, idx);
+ if (tonumber(o, &n)) {
+ lua_Integer res;
+ lua_Number num = nvalue(o);
+ lua_number2integer(res, num);
+ return res;
+ }
+ else
+ return 0;
+}
+
+
+LUA_API int lua_toboolean (lua_State *L, int idx) {
+ const TValue *o = index2adr(L, idx);
+ return !l_isfalse(o);
+}
+
+
+LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
+ StkId o = index2adr(L, idx);
+ if (!ttisstring(o)) {
+ lua_lock(L); /* `luaV_tostring' may create a new string */
+ if (!luaV_tostring(L, o)) { /* conversion failed? */
+ if (len != NULL) *len = 0;
+ lua_unlock(L);
+ return NULL;
+ }
+ luaC_checkGC(L);
+ o = index2adr(L, idx); /* previous call may reallocate the stack */
+ lua_unlock(L);
+ }
+ if (len != NULL) *len = tsvalue(o)->len;
+ return svalue(o);
+}
+
+
+LUA_API size_t lua_objlen (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TSTRING: return tsvalue(o)->len;
+ case LUA_TUSERDATA: return uvalue(o)->len;
+ case LUA_TTABLE: return luaH_getn(hvalue(o));
+ case LUA_TNUMBER: {
+ size_t l;
+ lua_lock(L); /* `luaV_tostring' may create a new string */
+ l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0);
+ lua_unlock(L);
+ return l;
+ }
+ default: return 0;
+ }
+}
+
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (!iscfunction(o)) ? NULL : clvalue(o)->c.f;
+}
+
+
+LUA_API void *lua_touserdata (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: return (rawuvalue(o) + 1);
+ case LUA_TLIGHTUSERDATA: return pvalue(o);
+ default: return NULL;
+ }
+}
+
+
+LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ return (!ttisthread(o)) ? NULL : thvalue(o);
+}
+
+
+LUA_API const void *lua_topointer (lua_State *L, int idx) {
+ StkId o = index2adr(L, idx);
+ switch (ttype(o)) {
+ case LUA_TTABLE: return hvalue(o);
+ case LUA_TFUNCTION: return clvalue(o);
+ case LUA_TTHREAD: return thvalue(o);
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ return lua_touserdata(L, idx);
+ default: return NULL;
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ lua_lock(L);
+ setnilvalue(L->top);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
+ lua_lock(L);
+ setnvalue(L->top, n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
+ lua_lock(L);
+ setnvalue(L->top, cast_num(n));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ lua_lock(L);
+ luaC_checkGC(L);
+ setsvalue2s(L, L->top, luaS_newlstr(L, s, len));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushstring (lua_State *L, const char *s) {
+ if (s == NULL)
+ lua_pushnil(L);
+ else
+ lua_pushlstring(L, s, strlen(s));
+}
+
+
+LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp) {
+ const char *ret;
+ lua_lock(L);
+ luaC_checkGC(L);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *ret;
+ va_list argp;
+ lua_lock(L);
+ luaC_checkGC(L);
+ va_start(argp, fmt);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ Closure *cl;
+ lua_lock(L);
+ luaC_checkGC(L);
+ api_checknelems(L, n);
+ cl = luaF_newCclosure(L, n, getcurrenv(L));
+ cl->c.f = fn;
+ L->top -= n;
+ while (n--)
+ setobj2n(L, &cl->c.upvalue[n], L->top+n);
+ setclvalue(L, L->top, cl);
+ lua_assert(iswhite(obj2gco(cl)));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushboolean (lua_State *L, int b) {
+ lua_lock(L);
+ setbvalue(L->top, (b != 0)); /* ensure that true is 1 */
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
+ lua_lock(L);
+ setpvalue(L->top, p);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_pushthread (lua_State *L) {
+ lua_lock(L);
+ setthvalue(L, L->top, L);
+ api_incr_top(L);
+ lua_unlock(L);
+ return (G(L)->mainthread == L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+LUA_API void lua_gettable (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ luaV_gettable(L, t, L->top - 1, L->top - 1);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
+ StkId t;
+ TValue key;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ setsvalue(L, &key, luaS_new(L, k));
+ luaV_gettable(L, t, &key, L->top);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawget (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawgeti (lua_State *L, int idx, int n) {
+ StkId o;
+ lua_lock(L);
+ o = index2adr(L, idx);
+ api_check(L, ttistable(o));
+ setobj2s(L, L->top, luaH_getnum(hvalue(o), n));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
+ lua_lock(L);
+ luaC_checkGC(L);
+ sethvalue(L, L->top, luaH_new(L, narray, nrec));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_getmetatable (lua_State *L, int objindex) {
+ const TValue *obj;
+ Table *mt = NULL;
+ int res;
+ lua_lock(L);
+ obj = index2adr(L, objindex);
+ switch (ttype(obj)) {
+ case LUA_TTABLE:
+ mt = hvalue(obj)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(obj)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(obj)];
+ break;
+ }
+ if (mt == NULL)
+ res = 0;
+ else {
+ sethvalue(L, L->top, mt);
+ api_incr_top(L);
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_getfenv (lua_State *L, int idx) {
+ StkId o;
+ lua_lock(L);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ switch (ttype(o)) {
+ case LUA_TFUNCTION:
+ sethvalue(L, L->top, clvalue(o)->c.env);
+ break;
+ case LUA_TUSERDATA:
+ sethvalue(L, L->top, uvalue(o)->env);
+ break;
+ case LUA_TTHREAD:
+ setobj2s(L, L->top, gt(thvalue(o)));
+ break;
+ default:
+ setnilvalue(L->top);
+ break;
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+
+LUA_API void lua_settable (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ luaV_settable(L, t, L->top - 2, L->top - 1);
+ L->top -= 2; /* pop index and value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
+ StkId t;
+ TValue key;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = index2adr(L, idx);
+ api_checkvalidindex(L, t);
+ setsvalue(L, &key, luaS_new(L, k));
+ luaV_settable(L, t, &key, L->top - 1);
+ L->top--; /* pop value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int idx) {
+ StkId t;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
+ luaC_barriert(L, hvalue(t), L->top-1);
+ L->top -= 2;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int idx, int n) {
+ StkId o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_check(L, ttistable(o));
+ setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);
+ luaC_barriert(L, hvalue(o), L->top-1);
+ L->top--;
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_setmetatable (lua_State *L, int objindex) {
+ TValue *obj;
+ Table *mt;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ obj = index2adr(L, objindex);
+ api_checkvalidindex(L, obj);
+ if (ttisnil(L->top - 1))
+ mt = NULL;
+ else {
+ api_check(L, ttistable(L->top - 1));
+ mt = hvalue(L->top - 1);
+ }
+ switch (ttype(obj)) {
+ case LUA_TTABLE: {
+ hvalue(obj)->metatable = mt;
+ if (mt)
+ luaC_objbarriert(L, hvalue(obj), mt);
+ break;
+ }
+ case LUA_TUSERDATA: {
+ uvalue(obj)->metatable = mt;
+ if (mt)
+ luaC_objbarrier(L, rawuvalue(obj), mt);
+ break;
+ }
+ default: {
+ G(L)->mt[ttype(obj)] = mt;
+ break;
+ }
+ }
+ L->top--;
+ lua_unlock(L);
+ return 1;
+}
+
+
+LUA_API int lua_setfenv (lua_State *L, int idx) {
+ StkId o;
+ int res = 1;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2adr(L, idx);
+ api_checkvalidindex(L, o);
+ api_check(L, ttistable(L->top - 1));
+ switch (ttype(o)) {
+ case LUA_TFUNCTION:
+ clvalue(o)->c.env = hvalue(L->top - 1);
+ break;
+ case LUA_TUSERDATA:
+ uvalue(o)->env = hvalue(L->top - 1);
+ break;
+ case LUA_TTHREAD:
+ sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));
+ break;
+ default:
+ res = 0;
+ break;
+ }
+ if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
+ L->top--;
+ lua_unlock(L);
+ return res;
+}
+
+
+/*
+** `load' and `call' functions (run Lua code)
+*/
+
+
+#define adjustresults(L,nres) \
+ { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; }
+
+
+#define checkresults(L,na,nr) \
+ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)))
+
+
+LUA_API void lua_call (lua_State *L, int nargs, int nresults) {
+ StkId func;
+ lua_lock(L);
+ api_checknelems(L, nargs+1);
+ checkresults(L, nargs, nresults);
+ func = L->top - (nargs+1);
+ luaD_call(L, func, nresults);
+ adjustresults(L, nresults);
+ lua_unlock(L);
+}
+
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to `f_call' */
+ StkId func;
+ int nresults;
+};
+
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = cast(struct CallS *, ud);
+ luaD_call(L, c->func, c->nresults);
+}
+
+
+
+LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
+ struct CallS c;
+ int status;
+ ptrdiff_t func;
+ lua_lock(L);
+ api_checknelems(L, nargs+1);
+ checkresults(L, nargs, nresults);
+ if (errfunc == 0)
+ func = 0;
+ else {
+ StkId o = index2adr(L, errfunc);
+ api_checkvalidindex(L, o);
+ func = savestack(L, o);
+ }
+ c.func = L->top - (nargs+1); /* function to be called */
+ c.nresults = nresults;
+ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
+ adjustresults(L, nresults);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** Execute a protected C call.
+*/
+struct CCallS { /* data to `f_Ccall' */
+ lua_CFunction func;
+ void *ud;
+};
+
+
+static void f_Ccall (lua_State *L, void *ud) {
+ struct CCallS *c = cast(struct CCallS *, ud);
+ Closure *cl;
+ cl = luaF_newCclosure(L, 0, getcurrenv(L));
+ cl->c.f = c->func;
+ setclvalue(L, L->top, cl); /* push function */
+ api_incr_top(L);
+ setpvalue(L->top, c->ud); /* push only argument */
+ api_incr_top(L);
+ luaD_call(L, L->top - 2, 0);
+}
+
+
+LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) {
+ struct CCallS c;
+ int status;
+ lua_lock(L);
+ c.func = func;
+ c.ud = ud;
+ status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
+ const char *chunkname) {
+ ZIO z;
+ int status;
+ lua_lock(L);
+ if (!chunkname) chunkname = "?";
+ luaZ_init(L, &z, reader, data);
+ status = luaD_protectedparser(L, &z, chunkname);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {
+ int status;
+ TValue *o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = L->top - 1;
+ if (isLfunction(o))
+ status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);
+ else
+ status = 1;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_status (lua_State *L) {
+ return L->status;
+}
+
+
+/*
+** Garbage-collection function
+*/
+
+LUA_API int lua_gc (lua_State *L, int what, int data) {
+ int res = 0;
+ global_State *g;
+ lua_lock(L);
+ g = G(L);
+ switch (what) {
+ case LUA_GCSTOP: {
+ g->GCthreshold = MAX_LUMEM;
+ break;
+ }
+ case LUA_GCRESTART: {
+ g->GCthreshold = g->totalbytes;
+ break;
+ }
+ case LUA_GCCOLLECT: {
+ luaC_fullgc(L);
+ break;
+ }
+ case LUA_GCCOUNT: {
+ /* GC values are expressed in Kbytes: #bytes/2^10 */
+ res = cast_int(g->totalbytes >> 10);
+ break;
+ }
+ case LUA_GCCOUNTB: {
+ res = cast_int(g->totalbytes & 0x3ff);
+ break;
+ }
+ case LUA_GCSTEP: {
+ lu_mem a = (cast(lu_mem, data) << 10);
+ if (a <= g->totalbytes)
+ g->GCthreshold = g->totalbytes - a;
+ else
+ g->GCthreshold = 0;
+ while (g->GCthreshold <= g->totalbytes)
+ luaC_step(L);
+ if (g->gcstate == GCSpause) /* end of cycle? */
+ res = 1; /* signal it */
+ break;
+ }
+ case LUA_GCSETPAUSE: {
+ res = g->gcpause;
+ g->gcpause = data;
+ break;
+ }
+ case LUA_GCSETSTEPMUL: {
+ res = g->gcstepmul;
+ g->gcstepmul = data;
+ break;
+ }
+ default: res = -1; /* invalid option */
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+
+/*
+** miscellaneous functions
+*/
+
+
+LUA_API int lua_error (lua_State *L) {
+ lua_lock(L);
+ api_checknelems(L, 1);
+ luaG_errormsg(L);
+ lua_unlock(L);
+ return 0; /* to avoid warnings */
+}
+
+
+LUA_API int lua_next (lua_State *L, int idx) {
+ StkId t;
+ int more;
+ lua_lock(L);
+ t = index2adr(L, idx);
+ api_check(L, ttistable(t));
+ more = luaH_next(L, hvalue(t), L->top - 1);
+ if (more) {
+ api_incr_top(L);
+ }
+ else /* no more elements */
+ L->top -= 1; /* remove key */
+ lua_unlock(L);
+ return more;
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ lua_lock(L);
+ api_checknelems(L, n);
+ if (n >= 2) {
+ luaC_checkGC(L);
+ luaV_concat(L, n, cast_int(L->top - L->base) - 1);
+ L->top -= (n-1);
+ }
+ else if (n == 0) { /* push empty string */
+ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
+ api_incr_top(L);
+ }
+ /* else n == 1; nothing to do */
+ lua_unlock(L);
+}
+
+
+LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
+ lua_Alloc f;
+ lua_lock(L);
+ if (ud) *ud = G(L)->ud;
+ f = G(L)->frealloc;
+ lua_unlock(L);
+ return f;
+}
+
+
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
+ lua_lock(L);
+ G(L)->ud = ud;
+ G(L)->frealloc = f;
+ lua_unlock(L);
+}
+
+
+LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
+ Udata *u;
+ lua_lock(L);
+ luaC_checkGC(L);
+ u = luaS_newudata(L, size, getcurrenv(L));
+ setuvalue(L, L->top, u);
+ api_incr_top(L);
+ lua_unlock(L);
+ return u + 1;
+}
+
+
+
+
+static const char *aux_upvalue (StkId fi, int n, TValue **val) {
+ Closure *f;
+ if (!ttisfunction(fi)) return NULL;
+ f = clvalue(fi);
+ if (f->c.isC) {
+ if (!(1 <= n && n <= f->c.nupvalues)) return NULL;
+ *val = &f->c.upvalue[n-1];
+ return "";
+ }
+ else {
+ Proto *p = f->l.p;
+ if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
+ *val = f->l.upvals[n-1]->v;
+ return getstr(p->upvalues[n-1]);
+ }
+}
+
+
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val;
+ lua_lock(L);
+ name = aux_upvalue(index2adr(L, funcindex), n, &val);
+ if (name) {
+ setobj2s(L, L->top, val);
+ api_incr_top(L);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val;
+ StkId fi;
+ lua_lock(L);
+ fi = index2adr(L, funcindex);
+ api_checknelems(L, 1);
+ name = aux_upvalue(fi, n, &val);
+ if (name) {
+ L->top--;
+ setobj(L, val, L->top);
+ luaC_barrier(L, clvalue(fi), L->top);
+ }
+ lua_unlock(L);
+ return name;
+}
+
diff --git a/engines/sword25/util/lua/lapi.h b/engines/sword25/util/lua/lapi.h
new file mode 100644
index 0000000000..2c3fab244e
--- /dev/null
+++ b/engines/sword25/util/lua/lapi.h
@@ -0,0 +1,16 @@
+/*
+** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "lobject.h"
+
+
+LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);
+
+#endif
diff --git a/engines/sword25/util/lua/lauxlib.c b/engines/sword25/util/lua/lauxlib.c
new file mode 100644
index 0000000000..10f14e2c08
--- /dev/null
+++ b/engines/sword25/util/lua/lauxlib.c
@@ -0,0 +1,652 @@
+/*
+** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+*/
+
+#define lauxlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+
+
+#define FREELIST_REF 0 /* free list of references */
+
+
+/* convert a stack index to positive */
+#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
+ lua_gettop(L) + (i) + 1)
+
+
+/*
+** {======================================================
+** Error-report functions
+** =======================================================
+*/
+
+
+LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {
+ lua_Debug ar;
+ if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
+ return luaL_error(L, "bad argument #%d (%s)", narg, extramsg);
+ lua_getinfo(L, "n", &ar);
+ if (strcmp(ar.namewhat, "method") == 0) {
+ narg--; /* do not count `self' */
+ if (narg == 0) /* error is in the self argument itself? */
+ return luaL_error(L, "calling " LUA_QS " on bad self (%s)",
+ ar.name, extramsg);
+ }
+ if (ar.name == NULL)
+ ar.name = "?";
+ return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)",
+ narg, ar.name, extramsg);
+}
+
+
+LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {
+ const char *msg = lua_pushfstring(L, "%s expected, got %s",
+ tname, luaL_typename(L, narg));
+ return luaL_argerror(L, narg, msg);
+}
+
+
+static void tag_error (lua_State *L, int narg, int tag) {
+ luaL_typerror(L, narg, lua_typename(L, tag));
+}
+
+
+LUALIB_API void luaL_where (lua_State *L, int level) {
+ lua_Debug ar;
+ if (lua_getstack(L, level, &ar)) { /* check function at level */
+ lua_getinfo(L, "Sl", &ar); /* get info about it */
+ if (ar.currentline > 0) { /* is there info? */
+ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
+ return;
+ }
+ }
+ lua_pushliteral(L, ""); /* else, no information available... */
+}
+
+
+LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+ return lua_error(L);
+}
+
+/* }====================================================== */
+
+
+LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,
+ const char *const lst[]) {
+ const char *name = (def) ? luaL_optstring(L, narg, def) :
+ luaL_checkstring(L, narg);
+ int i;
+ for (i=0; lst[i]; i++)
+ if (strcmp(lst[i], name) == 0)
+ return i;
+ return luaL_argerror(L, narg,
+ lua_pushfstring(L, "invalid option " LUA_QS, name));
+}
+
+
+LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
+ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
+ if (!lua_isnil(L, -1)) /* name already in use? */
+ return 0; /* leave previous value on top, but return 0 */
+ lua_pop(L, 1);
+ lua_newtable(L); /* create metatable */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
+ return 1;
+}
+
+
+LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
+ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
+ lua_pop(L, 2); /* remove both metatables */
+ return p;
+ }
+ }
+ }
+ luaL_typerror(L, ud, tname); /* else error */
+ return NULL; /* to avoid warnings */
+}
+
+
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
+ if (!lua_checkstack(L, space))
+ luaL_error(L, "stack overflow (%s)", mes);
+}
+
+
+LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {
+ if (lua_type(L, narg) != t)
+ tag_error(L, narg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int narg) {
+ if (lua_type(L, narg) == LUA_TNONE)
+ luaL_argerror(L, narg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {
+ const char *s = lua_tolstring(L, narg, len);
+ if (!s) tag_error(L, narg, LUA_TSTRING);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_optlstring (lua_State *L, int narg,
+ const char *def, size_t *len) {
+ if (lua_isnoneornil(L, narg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_checklstring(L, narg, len);
+}
+
+
+LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
+ lua_Number d = lua_tonumber(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ tag_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {
+ return luaL_opt(L, luaL_checknumber, narg, def);
+}
+
+
+LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {
+ lua_Integer d = lua_tointeger(L, narg);
+ if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
+ tag_error(L, narg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,
+ lua_Integer def) {
+ return luaL_opt(L, luaL_checkinteger, narg, def);
+}
+
+
+LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
+ if (!lua_getmetatable(L, obj)) /* no metatable? */
+ return 0;
+ lua_pushstring(L, event);
+ lua_rawget(L, -2);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 2); /* remove metatable and metafield */
+ return 0;
+ }
+ else {
+ lua_remove(L, -2); /* remove only metatable */
+ return 1;
+ }
+}
+
+
+LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
+ obj = abs_index(L, obj);
+ if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
+ return 0;
+ lua_pushvalue(L, obj);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l) {
+ luaI_openlib(L, libname, l, 0);
+}
+
+
+static int libsize (const luaL_Reg *l) {
+ int size = 0;
+ for (; l->name; l++) size++;
+ return size;
+}
+
+
+LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup) {
+ if (libname) {
+ int size = libsize(l);
+ /* check whether lib already exists */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
+ lua_getfield(L, -1, libname); /* get _LOADED[libname] */
+ if (!lua_istable(L, -1)) { /* not found? */
+ lua_pop(L, 1); /* remove previous result */
+ /* try global variable (and create one if it does not exist) */
+ if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
+ luaL_error(L, "name conflict for module " LUA_QS, libname);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
+ }
+ lua_remove(L, -2); /* remove _LOADED table */
+ lua_insert(L, -(nup+1)); /* move library table to below upvalues */
+ }
+ for (; l->name; l++) {
+ int i;
+ for (i=0; i<nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -nup);
+ lua_pushcclosure(L, l->func, nup);
+ lua_setfield(L, -(nup+2), l->name);
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+
+
+/*
+** {======================================================
+** getn-setn: size for arrays
+** =======================================================
+*/
+
+#if defined(LUA_COMPAT_GETN)
+
+static int checkint (lua_State *L, int topop) {
+ int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;
+ lua_pop(L, topop);
+ return n;
+}
+
+
+static void getsizes (lua_State *L) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES");
+ if (lua_isnil(L, -1)) { /* no `size' table? */
+ lua_pop(L, 1); /* remove nil */
+ lua_newtable(L); /* create it */
+ lua_pushvalue(L, -1); /* `size' will be its own metatable */
+ lua_setmetatable(L, -2);
+ lua_pushliteral(L, "kv");
+ lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */
+ }
+}
+
+
+LUALIB_API void luaL_setn (lua_State *L, int t, int n) {
+ t = abs_index(L, t);
+ lua_pushliteral(L, "n");
+ lua_rawget(L, t);
+ if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */
+ lua_pushliteral(L, "n"); /* use it */
+ lua_pushinteger(L, n);
+ lua_rawset(L, t);
+ }
+ else { /* use `sizes' */
+ getsizes(L);
+ lua_pushvalue(L, t);
+ lua_pushinteger(L, n);
+ lua_rawset(L, -3); /* sizes[t] = n */
+ lua_pop(L, 1); /* remove `sizes' */
+ }
+}
+
+
+LUALIB_API int luaL_getn (lua_State *L, int t) {
+ int n;
+ t = abs_index(L, t);
+ lua_pushliteral(L, "n"); /* try t.n */
+ lua_rawget(L, t);
+ if ((n = checkint(L, 1)) >= 0) return n;
+ getsizes(L); /* else try sizes[t] */
+ lua_pushvalue(L, t);
+ lua_rawget(L, -2);
+ if ((n = checkint(L, 2)) >= 0) return n;
+ return (int)lua_objlen(L, t);
+}
+
+#endif
+
+/* }====================================================== */
+
+
+
+LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
+ const char *r) {
+ const char *wild;
+ size_t l = strlen(p);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while ((wild = strstr(s, p)) != NULL) {
+ luaL_addlstring(&b, s, wild - s); /* push prefix */
+ luaL_addstring(&b, r); /* push replacement in place of pattern */
+ s = wild + l; /* continue after `p' */
+ }
+ luaL_addstring(&b, s); /* push last suffix */
+ luaL_pushresult(&b);
+ return lua_tostring(L, -1);
+}
+
+
+LUALIB_API const char *luaL_findtable (lua_State *L, int idx,
+ const char *fname, int szhint) {
+ const char *e;
+ lua_pushvalue(L, idx);
+ do {
+ e = strchr(fname, '.');
+ if (e == NULL) e = fname + strlen(fname);
+ lua_pushlstring(L, fname, e - fname);
+ lua_rawget(L, -2);
+ if (lua_isnil(L, -1)) { /* no such field? */
+ lua_pop(L, 1); /* remove this nil */
+ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
+ lua_pushlstring(L, fname, e - fname);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4); /* set new table into field */
+ }
+ else if (!lua_istable(L, -1)) { /* field has a non-table value? */
+ lua_pop(L, 2); /* remove table and value */
+ return fname; /* return problematic part of the name */
+ }
+ lua_remove(L, -2); /* remove previous table */
+ fname = e + 1;
+ } while (*e == '.');
+ return NULL;
+}
+
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+#define bufflen(B) ((B)->p - (B)->buffer)
+#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
+
+#define LIMIT (LUA_MINSTACK/2)
+
+
+static int emptybuffer (luaL_Buffer *B) {
+ size_t l = bufflen(B);
+ if (l == 0) return 0; /* put nothing on stack */
+ else {
+ lua_pushlstring(B->L, B->buffer, l);
+ B->p = B->buffer;
+ B->lvl++;
+ return 1;
+ }
+}
+
+
+static void adjuststack (luaL_Buffer *B) {
+ if (B->lvl > 1) {
+ lua_State *L = B->L;
+ int toget = 1; /* number of levels to concat */
+ size_t toplen = lua_strlen(L, -1);
+ do {
+ size_t l = lua_strlen(L, -(toget+1));
+ if (B->lvl - toget + 1 >= LIMIT || toplen > l) {
+ toplen += l;
+ toget++;
+ }
+ else break;
+ } while (toget < B->lvl);
+ lua_concat(L, toget);
+ B->lvl = B->lvl - toget + 1;
+ }
+}
+
+
+LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
+ if (emptybuffer(B))
+ adjuststack(B);
+ return B->buffer;
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ while (l--)
+ luaL_addchar(B, *s++);
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ emptybuffer(B);
+ lua_concat(B->L, B->lvl);
+ B->lvl = 1;
+}
+
+
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t vl;
+ const char *s = lua_tolstring(L, -1, &vl);
+ if (vl <= bufffree(B)) { /* fit into buffer? */
+ memcpy(B->p, s, vl); /* put it there */
+ B->p += vl;
+ lua_pop(L, 1); /* remove from stack */
+ }
+ else {
+ if (emptybuffer(B))
+ lua_insert(L, -2); /* put buffer before new value */
+ B->lvl++; /* add new value into B stack */
+ adjuststack(B);
+ }
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->p = B->buffer;
+ B->lvl = 0;
+}
+
+/* }====================================================== */
+
+
+LUALIB_API int luaL_ref (lua_State *L, int t) {
+ int ref;
+ t = abs_index(L, t);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* remove from stack */
+ return LUA_REFNIL; /* `nil' has a unique fixed reference */
+ }
+ lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
+ ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
+ lua_pop(L, 1); /* remove it from stack */
+ if (ref != 0) { /* any free element? */
+ lua_rawgeti(L, t, ref); /* remove it from list */
+ lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
+ }
+ else { /* no free elements */
+ ref = (int)lua_objlen(L, t);
+ ref++; /* create new reference */
+ }
+ lua_rawseti(L, t, ref);
+ return ref;
+}
+
+
+LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
+ if (ref >= 0) {
+ t = abs_index(L, t);
+ lua_rawgeti(L, t, FREELIST_REF);
+ lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
+ lua_pushinteger(L, ref);
+ lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
+ }
+}
+
+
+
+/*
+** {======================================================
+** Load functions
+** =======================================================
+*/
+
+typedef struct LoadF {
+ int extraline;
+ FILE *f;
+ char buff[LUAL_BUFFERSIZE];
+} LoadF;
+
+
+static const char *getF (lua_State *L, void *ud, size_t *size) {
+ LoadF *lf = (LoadF *)ud;
+ (void)L;
+ if (lf->extraline) {
+ lf->extraline = 0;
+ *size = 1;
+ return "\n";
+ }
+ if (feof(lf->f)) return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
+ return (*size > 0) ? lf->buff : NULL;
+}
+
+
+static int errfile (lua_State *L, const char *what, int fnameindex) {
+ const char *serr = strerror(errno);
+ const char *filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+
+LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
+ LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ lf.extraline = 0;
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+ lf.f = fopen(filename, "r");
+ if (lf.f == NULL) return errfile(L, "open", fnameindex);
+ }
+ c = getc(lf.f);
+ if (c == '#') { /* Unix exec. file? */
+ lf.extraline = 1;
+ while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */
+ if (c == '\n') c = getc(lf.f);
+ }
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
+ /* skip eventual `#!...' */
+ while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
+ lf.extraline = 0;
+ }
+ ungetc(c, lf.f);
+ status = lua_load(L, getF, &lf, lua_tostring(L, -1));
+ readstatus = ferror(lf.f);
+ if (filename) fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from `lua_load' */
+ return errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+
+typedef struct LoadS {
+ const char *s;
+ size_t size;
+} LoadS;
+
+
+static const char *getS (lua_State *L, void *ud, size_t *size) {
+ LoadS *ls = (LoadS *)ud;
+ (void)L;
+ if (ls->size == 0) return NULL;
+ *size = ls->size;
+ ls->size = 0;
+ return ls->s;
+}
+
+
+LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,
+ const char *name) {
+ LoadS ls;
+ ls.s = buff;
+ ls.size = size;
+ return lua_load(L, getS, &ls, name);
+}
+
+
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {
+ return luaL_loadbuffer(L, s, strlen(s), s);
+}
+
+
+
+/* }====================================================== */
+
+
+static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+ (void)ud;
+ (void)osize;
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+}
+
+
+static int panic (lua_State *L) {
+ (void)L; /* to avoid warnings */
+ fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
+ lua_tostring(L, -1));
+ return 0;
+}
+
+
+LUALIB_API lua_State *luaL_newstate (void) {
+ lua_State *L = lua_newstate(l_alloc, NULL);
+ if (L) lua_atpanic(L, &panic);
+ return L;
+}
+
diff --git a/engines/sword25/util/lua/lauxlib.h b/engines/sword25/util/lua/lauxlib.h
new file mode 100644
index 0000000000..34258235db
--- /dev/null
+++ b/engines/sword25/util/lua/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#if defined(LUA_COMPAT_GETN)
+LUALIB_API int (luaL_getn) (lua_State *L, int t);
+LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
+#else
+#define luaL_getn(L,i) ((int)lua_objlen(L, i))
+#define luaL_setn(L,i,j) ((void)0) /* no op! */
+#endif
+
+#if defined(LUA_COMPAT_OPENLIB)
+#define luaI_openlib luaL_openlib
+#endif
+
+
+/* extra error code for `luaL_load' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+
+LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup);
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l);
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
+LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
+LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
+ const char *name);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
+ const char *r);
+
+LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
+ const char *fname, int szhint);
+
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_argcheck(L, cond,numarg,extramsg) \
+ ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int lvl; /* number of strings in the stack (level) */
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+/* compatibility only */
+#define luaL_putchar(B,c) luaL_addchar(B,c)
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+/* compatibility with ref system */
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
+ (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
+
+#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
+
+#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
+
+
+#define luaL_reg luaL_Reg
+
+#endif
+
+
diff --git a/engines/sword25/util/lua/lbaselib.c b/engines/sword25/util/lua/lbaselib.c
new file mode 100644
index 0000000000..8f97a1c246
--- /dev/null
+++ b/engines/sword25/util/lua/lbaselib.c
@@ -0,0 +1,654 @@
+/*
+** $Id: lbaselib.c,v 1.191.1.4 2008/01/20 13:53:22 roberto Exp $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lbaselib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+/*
+** If your system does not support `stdout', you can just remove this function.
+** If you need, you can define your own `print' function, following this
+** model but changing `fputs' to put the strings at a proper place
+** (a console window or a log file, for instance).
+*/
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ lua_getglobal(L, "tostring");
+ for (i=1; i<=n; i++) {
+ const char *s;
+ lua_pushvalue(L, -1); /* function to be called */
+ lua_pushvalue(L, i); /* value to print */
+ lua_call(L, 1, 1);
+ s = lua_tostring(L, -1); /* get result */
+ if (s == NULL)
+ return luaL_error(L, LUA_QL("tostring") " must return a string to "
+ LUA_QL("print"));
+ lua_pop(L, 1); /* pop result */
+ }
+ return 0;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ int base = luaL_optint(L, 2, 10);
+ if (base == 10) { /* standard conversion */
+ luaL_checkany(L, 1);
+ if (lua_isnumber(L, 1)) {
+ lua_pushnumber(L, lua_tonumber(L, 1));
+ return 1;
+ }
+ }
+ else {
+ const char *s1 = luaL_checkstring(L, 1);
+ char *s2;
+ unsigned long n;
+ luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
+ n = strtoul(s1, &s2, base);
+ if (s1 != s2) { /* at least one valid digit? */
+ while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */
+ if (*s2 == '\0') { /* no invalid trailing characters? */
+ lua_pushnumber(L, (lua_Number)n);
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L); /* else not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ int level = luaL_optint(L, 2, 1);
+ lua_settop(L, 1);
+ if (lua_isstring(L, 1) && level > 0) { /* add extra information? */
+ luaL_where(L, level);
+ lua_pushvalue(L, 1);
+ lua_concat(L, 2);
+ }
+ return lua_error(L);
+}
+
+
+static int luaB_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L);
+ return 1; /* no metatable */
+ }
+ luaL_getmetafield(L, 1, "__metatable");
+ return 1; /* returns either __metatable field (if present) or metatable */
+}
+
+
+static int luaB_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
+ "nil or table expected");
+ if (luaL_getmetafield(L, 1, "__metatable"))
+ luaL_error(L, "cannot change a protected metatable");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1;
+}
+
+
+static void getfunc (lua_State *L, int opt) {
+ if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
+ else {
+ lua_Debug ar;
+ int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
+ luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
+ if (lua_getstack(L, level, &ar) == 0)
+ luaL_argerror(L, 1, "invalid level");
+ lua_getinfo(L, "f", &ar);
+ if (lua_isnil(L, -1))
+ luaL_error(L, "no function environment for tail call at level %d",
+ level);
+ }
+}
+
+
+static int luaB_getfenv (lua_State *L) {
+ getfunc(L, 1);
+ if (lua_iscfunction(L, -1)) /* is a C function? */
+ lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
+ else
+ lua_getfenv(L, -1);
+ return 1;
+}
+
+
+static int luaB_setfenv (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ getfunc(L, 0);
+ lua_pushvalue(L, 2);
+ if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {
+ /* change environment of current thread */
+ lua_pushthread(L);
+ lua_insert(L, -2);
+ lua_setfenv(L, -2);
+ return 0;
+ }
+ else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
+ luaL_error(L,
+ LUA_QL("setfenv") " cannot change environment of given object");
+ return 1;
+}
+
+
+static int luaB_rawequal (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_checkany(L, 2);
+ lua_pushboolean(L, lua_rawequal(L, 1, 2));
+ return 1;
+}
+
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_rawget(L, 1);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_settop(L, 3);
+ lua_rawset(L, 1);
+ return 1;
+}
+
+
+static int luaB_gcinfo (lua_State *L) {
+ lua_pushinteger(L, lua_getgccount(L));
+ return 1;
+}
+
+
+static int luaB_collectgarbage (lua_State *L) {
+ static const char *const opts[] = {"stop", "restart", "collect",
+ "count", "step", "setpause", "setstepmul", NULL};
+ static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
+ LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};
+ int o = luaL_checkoption(L, 1, "collect", opts);
+ int ex = luaL_optint(L, 2, 0);
+ int res = lua_gc(L, optsnum[o], ex);
+ switch (optsnum[o]) {
+ case LUA_GCCOUNT: {
+ int b = lua_gc(L, LUA_GCCOUNTB, 0);
+ lua_pushnumber(L, res + ((lua_Number)b/1024));
+ return 1;
+ }
+ case LUA_GCSTEP: {
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ default: {
+ lua_pushnumber(L, res);
+ return 1;
+ }
+ }
+}
+
+
+static int luaB_type (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushstring(L, luaL_typename(L, 1));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int luaB_pairs (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushnil(L); /* and initial value */
+ return 3;
+}
+
+
+static int ipairsaux (lua_State *L) {
+ int i = luaL_checkint(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i++; /* next value */
+ lua_pushinteger(L, i);
+ lua_rawgeti(L, 1, i);
+ return (lua_isnil(L, -1)) ? 0 : 2;
+}
+
+
+static int luaB_ipairs (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushinteger(L, 0); /* and initial value */
+ return 3;
+}
+
+
+static int load_aux (lua_State *L, int status) {
+ if (status == 0) /* OK? */
+ return 1;
+ else {
+ lua_pushnil(L);
+ lua_insert(L, -2); /* put before error message */
+ return 2; /* return nil plus error message */
+ }
+}
+
+
+static int luaB_loadstring (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ const char *chunkname = luaL_optstring(L, 2, s);
+ return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
+}
+
+
+static int luaB_loadfile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ return load_aux(L, luaL_loadfile(L, fname));
+}
+
+
+/*
+** Reader for generic `load' function: `lua_load' uses the
+** stack for internal stuff, so the reader cannot change the
+** stack top. Instead, it keeps its resulting string in a
+** reserved slot inside the stack.
+*/
+static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
+ (void)ud; /* to avoid warnings */
+ luaL_checkstack(L, 2, "too many nested functions");
+ lua_pushvalue(L, 1); /* get function */
+ lua_call(L, 0, 1); /* call it */
+ if (lua_isnil(L, -1)) {
+ *size = 0;
+ return NULL;
+ }
+ else if (lua_isstring(L, -1)) {
+ lua_replace(L, 3); /* save string in a reserved stack slot */
+ return lua_tolstring(L, 3, size);
+ }
+ else luaL_error(L, "reader function must return a string");
+ return NULL; /* to avoid warnings */
+}
+
+
+static int luaB_load (lua_State *L) {
+ int status;
+ const char *cname = luaL_optstring(L, 2, "=(load)");
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 3); /* function, eventual name, plus one reserved slot */
+ status = lua_load(L, generic_reader, NULL, cname);
+ return load_aux(L, status);
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ int n = lua_gettop(L);
+ if (luaL_loadfile(L, fname) != 0) lua_error(L);
+ lua_call(L, 0, LUA_MULTRET);
+ return lua_gettop(L) - n;
+}
+
+
+static int luaB_assert (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_toboolean(L, 1))
+ return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
+ return lua_gettop(L);
+}
+
+
+static int luaB_unpack (lua_State *L) {
+ int i, e, n;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i = luaL_optint(L, 2, 1);
+ e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
+ n = e - i + 1; /* number of elements */
+ if (n <= 0) return 0; /* empty range */
+ luaL_checkstack(L, n, "table too big to unpack");
+ for (; i<=e; i++) /* push arg[i...e] */
+ lua_rawgeti(L, 1, i);
+ return n;
+}
+
+
+static int luaB_select (lua_State *L) {
+ int n = lua_gettop(L);
+ if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
+ lua_pushinteger(L, n-1);
+ return 1;
+ }
+ else {
+ int i = luaL_checkint(L, 1);
+ if (i < 0) i = n + i;
+ else if (i > n) i = n;
+ luaL_argcheck(L, 1 <= i, 1, "index out of range");
+ return n - i;
+ }
+}
+
+
+static int luaB_pcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 1);
+ status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
+ lua_pushboolean(L, (status == 0));
+ lua_insert(L, 1);
+ return lua_gettop(L); /* return status + all results */
+}
+
+
+static int luaB_xpcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_insert(L, 1); /* put error function under function to be called */
+ status = lua_pcall(L, 0, LUA_MULTRET, 1);
+ lua_pushboolean(L, (status == 0));
+ lua_replace(L, 1);
+ return lua_gettop(L); /* return status + all results */
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */
+ return 1; /* use its value */
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ lua_pushstring(L, lua_tostring(L, 1));
+ break;
+ case LUA_TSTRING:
+ lua_pushvalue(L, 1);
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
+ break;
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ default:
+ lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
+ break;
+ }
+ return 1;
+}
+
+
+static int luaB_newproxy (lua_State *L) {
+ lua_settop(L, 1);
+ lua_newuserdata(L, 0); /* create proxy */
+ if (lua_toboolean(L, 1) == 0)
+ return 1; /* no metatable */
+ else if (lua_isboolean(L, 1)) {
+ lua_newtable(L); /* create a new metatable `m' ... */
+ lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */
+ lua_pushboolean(L, 1);
+ lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */
+ }
+ else {
+ int validproxy = 0; /* to check if weaktable[metatable(u)] == true */
+ if (lua_getmetatable(L, 1)) {
+ lua_rawget(L, lua_upvalueindex(1));
+ validproxy = lua_toboolean(L, -1);
+ lua_pop(L, 1); /* remove value */
+ }
+ luaL_argcheck(L, validproxy, 1, "boolean or proxy expected");
+ lua_getmetatable(L, 1); /* metatable is valid; get it */
+ }
+ lua_setmetatable(L, 2);
+ return 1;
+}
+
+
+static const luaL_Reg base_funcs[] = {
+ {"assert", luaB_assert},
+ {"collectgarbage", luaB_collectgarbage},
+ {"dofile", luaB_dofile},
+ {"error", luaB_error},
+ {"gcinfo", luaB_gcinfo},
+ {"getfenv", luaB_getfenv},
+ {"getmetatable", luaB_getmetatable},
+ {"loadfile", luaB_loadfile},
+ {"load", luaB_load},
+ {"loadstring", luaB_loadstring},
+ {"next", luaB_next},
+ {"pcall", luaB_pcall},
+ {"print", luaB_print},
+ {"rawequal", luaB_rawequal},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"select", luaB_select},
+ {"setfenv", luaB_setfenv},
+ {"setmetatable", luaB_setmetatable},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"unpack", luaB_unpack},
+ {"xpcall", luaB_xpcall},
+ {NULL, NULL}
+};
+
+
+/*
+** {======================================================
+** Coroutine library
+** =======================================================
+*/
+
+#define CO_RUN 0 /* running */
+#define CO_SUS 1 /* suspended */
+#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */
+#define CO_DEAD 3
+
+static const char *const statnames[] =
+ {"running", "suspended", "normal", "dead"};
+
+static int costatus (lua_State *L, lua_State *co) {
+ if (L == co) return CO_RUN;
+ switch (lua_status(co)) {
+ case LUA_YIELD:
+ return CO_SUS;
+ case 0: {
+ lua_Debug ar;
+ if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
+ return CO_NOR; /* it is running */
+ else if (lua_gettop(co) == 0)
+ return CO_DEAD;
+ else
+ return CO_SUS; /* initial state */
+ }
+ default: /* some error occured */
+ return CO_DEAD;
+ }
+}
+
+
+static int luaB_costatus (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ luaL_argcheck(L, co, 1, "coroutine expected");
+ lua_pushstring(L, statnames[costatus(L, co)]);
+ return 1;
+}
+
+
+static int auxresume (lua_State *L, lua_State *co, int narg) {
+ int status = costatus(L, co);
+ if (!lua_checkstack(co, narg))
+ luaL_error(L, "too many arguments to resume");
+ if (status != CO_SUS) {
+ lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
+ return -1; /* error flag */
+ }
+ lua_xmove(L, co, narg);
+ lua_setlevel(L, co);
+ status = lua_resume(co, narg);
+ if (status == 0 || status == LUA_YIELD) {
+ int nres = lua_gettop(co);
+ if (!lua_checkstack(L, nres))
+ luaL_error(L, "too many results to resume");
+ lua_xmove(co, L, nres); /* move yielded values */
+ return nres;
+ }
+ else {
+ lua_xmove(co, L, 1); /* move error message */
+ return -1; /* error flag */
+ }
+}
+
+
+static int luaB_coresume (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ int r;
+ luaL_argcheck(L, co, 1, "coroutine expected");
+ r = auxresume(L, co, lua_gettop(L) - 1);
+ if (r < 0) {
+ lua_pushboolean(L, 0);
+ lua_insert(L, -2);
+ return 2; /* return false + error message */
+ }
+ else {
+ lua_pushboolean(L, 1);
+ lua_insert(L, -(r + 1));
+ return r + 1; /* return true + `resume' returns */
+ }
+}
+
+
+static int luaB_auxwrap (lua_State *L) {
+ lua_State *co = lua_tothread(L, lua_upvalueindex(1));
+ int r = auxresume(L, co, lua_gettop(L));
+ if (r < 0) {
+ if (lua_isstring(L, -1)) { /* error object is a string? */
+ luaL_where(L, 1); /* add extra info */
+ lua_insert(L, -2);
+ lua_concat(L, 2);
+ }
+ lua_error(L); /* propagate error */
+ }
+ return r;
+}
+
+
+static int luaB_cocreate (lua_State *L) {
+ lua_State *NL = lua_newthread(L);
+ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
+ "Lua function expected");
+ lua_pushvalue(L, 1); /* move function to top */
+ lua_xmove(L, NL, 1); /* move function from L to NL */
+ return 1;
+}
+
+
+static int luaB_cowrap (lua_State *L) {
+ luaB_cocreate(L);
+ lua_pushcclosure(L, luaB_auxwrap, 1);
+ return 1;
+}
+
+
+static int luaB_yield (lua_State *L) {
+ return lua_yield(L, lua_gettop(L));
+}
+
+
+static int luaB_corunning (lua_State *L) {
+ if (lua_pushthread(L))
+ lua_pushnil(L); /* main thread is not a coroutine */
+ return 1;
+}
+
+
+static const luaL_Reg co_funcs[] = {
+ {"create", luaB_cocreate},
+ {"resume", luaB_coresume},
+ {"running", luaB_corunning},
+ {"status", luaB_costatus},
+ {"wrap", luaB_cowrap},
+ {"yield", luaB_yield},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+static void auxopen (lua_State *L, const char *name,
+ lua_CFunction f, lua_CFunction u) {
+ lua_pushcfunction(L, u);
+ /* BS25 ==== */
+ lua_pushstring(L, name);
+ lua_pushstring(L, "_next");
+ lua_concat(L, 2);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_GLOBALSINDEX);
+ /* ==== BS25 */
+ lua_pushcclosure(L, f, 1);
+ lua_setfield(L, -2, name);
+}
+
+
+static void base_open (lua_State *L) {
+ /* set global _G */
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_setglobal(L, "_G");
+ /* open lib into global table */
+ luaL_register(L, "_G", base_funcs);
+ lua_pushliteral(L, LUA_VERSION);
+ lua_setglobal(L, "_VERSION"); /* set global _VERSION */
+ /* `ipairs' and `pairs' need auxliliary functions as upvalues */
+ auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
+ auxopen(L, "pairs", luaB_pairs, luaB_next);
+ /* `newproxy' needs a weaktable as upvalue */
+ lua_createtable(L, 0, 1); /* new table `w' */
+ lua_pushvalue(L, -1); /* `w' will be its own metatable */
+ lua_setmetatable(L, -2);
+ lua_pushliteral(L, "kv");
+ lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
+ lua_pushcclosure(L, luaB_newproxy, 1);
+ lua_setglobal(L, "newproxy"); /* set global `newproxy' */
+}
+
+
+LUALIB_API int luaopen_base (lua_State *L) {
+ base_open(L);
+ luaL_register(L, LUA_COLIBNAME, co_funcs);
+ return 2;
+}
+
diff --git a/engines/sword25/util/lua/lcode.c b/engines/sword25/util/lua/lcode.c
new file mode 100644
index 0000000000..cff626b7fa
--- /dev/null
+++ b/engines/sword25/util/lua/lcode.c
@@ -0,0 +1,839 @@
+/*
+** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+
+#define lcode_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "ltable.h"
+
+
+#define hasjumps(e) ((e)->t != (e)->f)
+
+
+static int isnumeral(expdesc *e) {
+ return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
+}
+
+
+void luaK_nil (FuncState *fs, int from, int n) {
+ Instruction *previous;
+ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
+ if (fs->pc == 0) { /* function start? */
+ if (from >= fs->nactvar)
+ return; /* positions are already clean */
+ }
+ else {
+ previous = &fs->f->code[fs->pc-1];
+ if (GET_OPCODE(*previous) == OP_LOADNIL) {
+ int pfrom = GETARG_A(*previous);
+ int pto = GETARG_B(*previous);
+ if (pfrom <= from && from <= pto+1) { /* can connect both? */
+ if (from+n-1 > pto)
+ SETARG_B(*previous, from+n-1);
+ return;
+ }
+ }
+ }
+ }
+ luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
+}
+
+
+int luaK_jump (FuncState *fs) {
+ int jpc = fs->jpc; /* save list of jumps to here */
+ int j;
+ fs->jpc = NO_JUMP;
+ j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
+ luaK_concat(fs, &j, jpc); /* keep them on hold */
+ return j;
+}
+
+
+void luaK_ret (FuncState *fs, int first, int nret) {
+ luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
+}
+
+
+static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
+ luaK_codeABC(fs, op, A, B, C);
+ return luaK_jump(fs);
+}
+
+
+static void fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest-(pc+1);
+ lua_assert(dest != NO_JUMP);
+ if (abs(offset) > MAXARG_sBx)
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ SETARG_sBx(*jmp, offset);
+}
+
+
+/*
+** returns current `pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+*/
+int luaK_getlabel (FuncState *fs) {
+ fs->lasttarget = fs->pc;
+ return fs->pc;
+}
+
+
+static int getjump (FuncState *fs, int pc) {
+ int offset = GETARG_sBx(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+static Instruction *getjumpcontrol (FuncState *fs, int pc) {
+ Instruction *pi = &fs->f->code[pc];
+ if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
+ return pi-1;
+ else
+ return pi;
+}
+
+
+/*
+** check whether list has any jump that do not produce a value
+** (or produce an inverted value)
+*/
+static int need_value (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list)) {
+ Instruction i = *getjumpcontrol(fs, list);
+ if (GET_OPCODE(i) != OP_TESTSET) return 1;
+ }
+ return 0; /* not found */
+}
+
+
+static int patchtestreg (FuncState *fs, int node, int reg) {
+ Instruction *i = getjumpcontrol(fs, node);
+ if (GET_OPCODE(*i) != OP_TESTSET)
+ return 0; /* cannot patch other instructions */
+ if (reg != NO_REG && reg != GETARG_B(*i))
+ SETARG_A(*i, reg);
+ else /* no register to put value or register already has the value */
+ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
+
+ return 1;
+}
+
+
+static void removevalues (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list))
+ patchtestreg(fs, list, NO_REG);
+}
+
+
+static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
+ int dtarget) {
+ while (list != NO_JUMP) {
+ int next = getjump(fs, list);
+ if (patchtestreg(fs, list, reg))
+ fixjump(fs, list, vtarget);
+ else
+ fixjump(fs, list, dtarget); /* jump to default target */
+ list = next;
+ }
+}
+
+
+static void dischargejpc (FuncState *fs) {
+ patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
+ fs->jpc = NO_JUMP;
+}
+
+
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ if (target == fs->pc)
+ luaK_patchtohere(fs, list);
+ else {
+ lua_assert(target < fs->pc);
+ patchlistaux(fs, list, target, NO_REG, target);
+ }
+}
+
+
+void luaK_patchtohere (FuncState *fs, int list) {
+ luaK_getlabel(fs);
+ luaK_concat(fs, &fs->jpc, list);
+}
+
+
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (l2 == NO_JUMP) return;
+ else if (*l1 == NO_JUMP)
+ *l1 = l2;
+ else {
+ int list = *l1;
+ int next;
+ while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
+ list = next;
+ fixjump(fs, list, l2);
+ }
+}
+
+
+void luaK_checkstack (FuncState *fs, int n) {
+ int newstack = fs->freereg + n;
+ if (newstack > fs->f->maxstacksize) {
+ if (newstack >= MAXSTACK)
+ luaX_syntaxerror(fs->ls, "function or expression too complex");
+ fs->f->maxstacksize = cast_byte(newstack);
+ }
+}
+
+
+void luaK_reserveregs (FuncState *fs, int n) {
+ luaK_checkstack(fs, n);
+ fs->freereg += n;
+}
+
+
+static void freereg (FuncState *fs, int reg) {
+ if (!ISK(reg) && reg >= fs->nactvar) {
+ fs->freereg--;
+ lua_assert(reg == fs->freereg);
+ }
+}
+
+
+static void freeexp (FuncState *fs, expdesc *e) {
+ if (e->k == VNONRELOC)
+ freereg(fs, e->u.s.info);
+}
+
+
+static int addk (FuncState *fs, TValue *k, TValue *v) {
+ lua_State *L = fs->L;
+ TValue *idx = luaH_set(L, fs->h, k);
+ Proto *f = fs->f;
+ int oldsize = f->sizek;
+ if (ttisnumber(idx)) {
+ lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
+ return cast_int(nvalue(idx));
+ }
+ else { /* constant not found; create a new entry */
+ setnvalue(idx, cast_num(fs->nk));
+ luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
+ MAXARG_Bx, "constant table overflow");
+ while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
+ setobj(L, &f->k[fs->nk], v);
+ luaC_barrier(L, f, v);
+ return fs->nk++;
+ }
+}
+
+
+int luaK_stringK (FuncState *fs, TString *s) {
+ TValue o;
+ setsvalue(fs->L, &o, s);
+ return addk(fs, &o, &o);
+}
+
+
+int luaK_numberK (FuncState *fs, lua_Number r) {
+ TValue o;
+ setnvalue(&o, r);
+ return addk(fs, &o, &o);
+}
+
+
+static int boolK (FuncState *fs, int b) {
+ TValue o;
+ setbvalue(&o, b);
+ return addk(fs, &o, &o);
+}
+
+
+static int nilK (FuncState *fs) {
+ TValue k, v;
+ setnilvalue(&v);
+ /* cannot use nil as key; instead use table itself to represent nil */
+ sethvalue(fs->L, &k, fs->h);
+ return addk(fs, &k, &v);
+}
+
+
+void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ SETARG_C(getcode(fs, e), nresults+1);
+ }
+ else if (e->k == VVARARG) {
+ SETARG_B(getcode(fs, e), nresults+1);
+ SETARG_A(getcode(fs, e), fs->freereg);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+void luaK_setoneret (FuncState *fs, expdesc *e) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ e->k = VNONRELOC;
+ e->u.s.info = GETARG_A(getcode(fs, e));
+ }
+ else if (e->k == VVARARG) {
+ SETARG_B(getcode(fs, e), 2);
+ e->k = VRELOCABLE; /* can relocate its simple result */
+ }
+}
+
+
+void luaK_dischargevars (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VLOCAL: {
+ e->k = VNONRELOC;
+ break;
+ }
+ case VUPVAL: {
+ e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VGLOBAL: {
+ e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VINDEXED: {
+ freereg(fs, e->u.s.aux);
+ freereg(fs, e->u.s.info);
+ e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
+ e->k = VRELOCABLE;
+ break;
+ }
+ case VVARARG:
+ case VCALL: {
+ luaK_setoneret(fs, e);
+ break;
+ }
+ default: break; /* there is one value available (somewhere) */
+ }
+}
+
+
+static int code_label (FuncState *fs, int A, int b, int jump) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
+}
+
+
+static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: {
+ luaK_nil(fs, reg, 1);
+ break;
+ }
+ case VFALSE: case VTRUE: {
+ luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
+ break;
+ }
+ case VK: {
+ luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
+ break;
+ }
+ case VKNUM: {
+ luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
+ break;
+ }
+ case VRELOCABLE: {
+ Instruction *pc = &getcode(fs, e);
+ SETARG_A(*pc, reg);
+ break;
+ }
+ case VNONRELOC: {
+ if (reg != e->u.s.info)
+ luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
+ break;
+ }
+ default: {
+ lua_assert(e->k == VVOID || e->k == VJMP);
+ return; /* nothing to do... */
+ }
+ }
+ e->u.s.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+static void discharge2anyreg (FuncState *fs, expdesc *e) {
+ if (e->k != VNONRELOC) {
+ luaK_reserveregs(fs, 1);
+ discharge2reg(fs, e, fs->freereg-1);
+ }
+}
+
+
+static void exp2reg (FuncState *fs, expdesc *e, int reg) {
+ discharge2reg(fs, e, reg);
+ if (e->k == VJMP)
+ luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
+ if (hasjumps(e)) {
+ int final; /* position after whole expression */
+ int p_f = NO_JUMP; /* position of an eventual LOAD false */
+ int p_t = NO_JUMP; /* position of an eventual LOAD true */
+ if (need_value(fs, e->t) || need_value(fs, e->f)) {
+ int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
+ p_f = code_label(fs, reg, 0, 1);
+ p_t = code_label(fs, reg, 1, 0);
+ luaK_patchtohere(fs, fj);
+ }
+ final = luaK_getlabel(fs);
+ patchlistaux(fs, e->f, final, reg, p_f);
+ patchlistaux(fs, e->t, final, reg, p_t);
+ }
+ e->f = e->t = NO_JUMP;
+ e->u.s.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ freeexp(fs, e);
+ luaK_reserveregs(fs, 1);
+ exp2reg(fs, e, fs->freereg - 1);
+}
+
+
+int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ if (e->k == VNONRELOC) {
+ if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
+ if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
+ exp2reg(fs, e, e->u.s.info); /* put value on it */
+ return e->u.s.info;
+ }
+ }
+ luaK_exp2nextreg(fs, e); /* default */
+ return e->u.s.info;
+}
+
+
+void luaK_exp2val (FuncState *fs, expdesc *e) {
+ if (hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+ else
+ luaK_dischargevars(fs, e);
+}
+
+
+int luaK_exp2RK (FuncState *fs, expdesc *e) {
+ luaK_exp2val(fs, e);
+ switch (e->k) {
+ case VKNUM:
+ case VTRUE:
+ case VFALSE:
+ case VNIL: {
+ if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
+ e->u.s.info = (e->k == VNIL) ? nilK(fs) :
+ (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
+ boolK(fs, (e->k == VTRUE));
+ e->k = VK;
+ return RKASK(e->u.s.info);
+ }
+ else break;
+ }
+ case VK: {
+ if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
+ return RKASK(e->u.s.info);
+ else break;
+ }
+ default: break;
+ }
+ /* not a constant in the right range: put it in a register */
+ return luaK_exp2anyreg(fs, e);
+}
+
+
+void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
+ switch (var->k) {
+ case VLOCAL: {
+ freeexp(fs, ex);
+ exp2reg(fs, ex, var->u.s.info);
+ return;
+ }
+ case VUPVAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
+ break;
+ }
+ case VGLOBAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
+ break;
+ }
+ case VINDEXED: {
+ int e = luaK_exp2RK(fs, ex);
+ luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
+ break;
+ }
+ default: {
+ lua_assert(0); /* invalid var kind to store */
+ break;
+ }
+ }
+ freeexp(fs, ex);
+}
+
+
+void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
+ int func;
+ luaK_exp2anyreg(fs, e);
+ freeexp(fs, e);
+ func = fs->freereg;
+ luaK_reserveregs(fs, 2);
+ luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
+ freeexp(fs, key);
+ e->u.s.info = func;
+ e->k = VNONRELOC;
+}
+
+
+static void invertjump (FuncState *fs, expdesc *e) {
+ Instruction *pc = getjumpcontrol(fs, e->u.s.info);
+ lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+ GET_OPCODE(*pc) != OP_TEST);
+ SETARG_A(*pc, !(GETARG_A(*pc)));
+}
+
+
+static int jumponcond (FuncState *fs, expdesc *e, int cond) {
+ if (e->k == VRELOCABLE) {
+ Instruction ie = getcode(fs, e);
+ if (GET_OPCODE(ie) == OP_NOT) {
+ fs->pc--; /* remove previous OP_NOT */
+ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
+}
+
+
+void luaK_goiftrue (FuncState *fs, expdesc *e) {
+ int pc; /* pc of last jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VK: case VKNUM: case VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ case VFALSE: {
+ pc = luaK_jump(fs); /* always jump */
+ break;
+ }
+ case VJMP: {
+ invertjump(fs, e);
+ pc = e->u.s.info;
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 0);
+ break;
+ }
+ }
+ luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
+ luaK_patchtohere(fs, e->t);
+ e->t = NO_JUMP;
+}
+
+
+static void luaK_goiffalse (FuncState *fs, expdesc *e) {
+ int pc; /* pc of last jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ case VTRUE: {
+ pc = luaK_jump(fs); /* always jump */
+ break;
+ }
+ case VJMP: {
+ pc = e->u.s.info;
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 1);
+ break;
+ }
+ }
+ luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
+ luaK_patchtohere(fs, e->f);
+ e->f = NO_JUMP;
+}
+
+
+static void codenot (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ e->k = VTRUE;
+ break;
+ }
+ case VK: case VKNUM: case VTRUE: {
+ e->k = VFALSE;
+ break;
+ }
+ case VJMP: {
+ invertjump(fs, e);
+ break;
+ }
+ case VRELOCABLE:
+ case VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
+ e->k = VRELOCABLE;
+ break;
+ }
+ default: {
+ lua_assert(0); /* cannot happen */
+ break;
+ }
+ }
+ /* interchange true and false lists */
+ { int temp = e->f; e->f = e->t; e->t = temp; }
+ removevalues(fs, e->f);
+ removevalues(fs, e->t);
+}
+
+
+void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
+ t->u.s.aux = luaK_exp2RK(fs, k);
+ t->k = VINDEXED;
+}
+
+
+static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
+ lua_Number v1, v2, r;
+ if (!isnumeral(e1) || !isnumeral(e2)) return 0;
+ v1 = e1->u.nval;
+ v2 = e2->u.nval;
+ switch (op) {
+ case OP_ADD: r = luai_numadd(v1, v2); break;
+ case OP_SUB: r = luai_numsub(v1, v2); break;
+ case OP_MUL: r = luai_nummul(v1, v2); break;
+ case OP_DIV:
+ if (v2 == 0) return 0; /* do not attempt to divide by 0 */
+ r = luai_numdiv(v1, v2); break;
+ case OP_MOD:
+ if (v2 == 0) return 0; /* do not attempt to divide by 0 */
+ r = luai_nummod(v1, v2); break;
+ case OP_POW: r = luai_numpow(v1, v2); break;
+ case OP_UNM: r = luai_numunm(v1); break;
+ case OP_LEN: return 0; /* no constant folding for 'len' */
+ default: lua_assert(0); r = 0; break;
+ }
+ if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
+ e1->u.nval = r;
+ return 1;
+}
+
+
+static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
+ if (constfolding(op, e1, e2))
+ return;
+ else {
+ int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
+ int o1 = luaK_exp2RK(fs, e1);
+ if (o1 > o2) {
+ freeexp(fs, e1);
+ freeexp(fs, e2);
+ }
+ else {
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ }
+ e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
+ e1->k = VRELOCABLE;
+ }
+}
+
+
+static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
+ expdesc *e2) {
+ int o1 = luaK_exp2RK(fs, e1);
+ int o2 = luaK_exp2RK(fs, e2);
+ freeexp(fs, e2);
+ freeexp(fs, e1);
+ if (cond == 0 && op != OP_EQ) {
+ int temp; /* exchange args to replace by `<' or `<=' */
+ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
+ cond = 1;
+ }
+ e1->u.s.info = condjump(fs, op, cond, o1, o2);
+ e1->k = VJMP;
+}
+
+
+void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
+ expdesc e2;
+ e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
+ switch (op) {
+ case OPR_MINUS: {
+ if (!isnumeral(e))
+ luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
+ codearith(fs, OP_UNM, e, &e2);
+ break;
+ }
+ case OPR_NOT: codenot(fs, e); break;
+ case OPR_LEN: {
+ luaK_exp2anyreg(fs, e); /* cannot operate on constants */
+ codearith(fs, OP_LEN, e, &e2);
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
+ switch (op) {
+ case OPR_AND: {
+ luaK_goiftrue(fs, v);
+ break;
+ }
+ case OPR_OR: {
+ luaK_goiffalse(fs, v);
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
+ break;
+ }
+ case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
+ case OPR_MOD: case OPR_POW: {
+ if (!isnumeral(v)) luaK_exp2RK(fs, v);
+ break;
+ }
+ default: {
+ luaK_exp2RK(fs, v);
+ break;
+ }
+ }
+}
+
+
+void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
+ switch (op) {
+ case OPR_AND: {
+ lua_assert(e1->t == NO_JUMP); /* list must be closed */
+ luaK_dischargevars(fs, e2);
+ luaK_concat(fs, &e2->f, e1->f);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_OR: {
+ lua_assert(e1->f == NO_JUMP); /* list must be closed */
+ luaK_dischargevars(fs, e2);
+ luaK_concat(fs, &e2->t, e1->t);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2val(fs, e2);
+ if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
+ lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
+ freeexp(fs, e1);
+ SETARG_B(getcode(fs, e2), e1->u.s.info);
+ e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
+ }
+ else {
+ luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
+ codearith(fs, OP_CONCAT, e1, e2);
+ }
+ break;
+ }
+ case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
+ case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
+ case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
+ case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
+ case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
+ case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
+ case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
+ case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
+ case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
+ case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
+ case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
+ case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
+ default: lua_assert(0);
+ }
+}
+
+
+void luaK_fixline (FuncState *fs, int line) {
+ fs->f->lineinfo[fs->pc - 1] = line;
+}
+
+
+static int luaK_code (FuncState *fs, Instruction i, int line) {
+ Proto *f = fs->f;
+ dischargejpc(fs); /* `pc' will change */
+ /* put new instruction in code array */
+ luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
+ MAX_INT, "code size overflow");
+ f->code[fs->pc] = i;
+ /* save corresponding line information */
+ luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
+ MAX_INT, "code size overflow");
+ f->lineinfo[fs->pc] = line;
+ return fs->pc++;
+}
+
+
+int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
+ lua_assert(getOpMode(o) == iABC);
+ lua_assert(getBMode(o) != OpArgN || b == 0);
+ lua_assert(getCMode(o) != OpArgN || c == 0);
+ return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
+}
+
+
+int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
+ lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
+ lua_assert(getCMode(o) == OpArgN);
+ return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
+}
+
+
+void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
+ int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
+ int b = (tostore == LUA_MULTRET) ? 0 : tostore;
+ lua_assert(tostore != 0);
+ if (c <= MAXARG_C)
+ luaK_codeABC(fs, OP_SETLIST, base, b, c);
+ else {
+ luaK_codeABC(fs, OP_SETLIST, base, b, 0);
+ luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
+ }
+ fs->freereg = base + 1; /* free registers with list values */
+}
+
diff --git a/engines/sword25/util/lua/lcode.h b/engines/sword25/util/lua/lcode.h
new file mode 100644
index 0000000000..b941c60721
--- /dev/null
+++ b/engines/sword25/util/lua/lcode.h
@@ -0,0 +1,76 @@
+/*
+** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums
+*/
+typedef enum BinOpr {
+ OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
+ OPR_CONCAT,
+ OPR_NE, OPR_EQ,
+ OPR_LT, OPR_LE, OPR_GT, OPR_GE,
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+
+typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
+
+
+#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info])
+
+#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
+
+#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
+
+LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
+LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
+LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
+LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
+LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
+LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
+LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
+LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);
+LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
+LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
+LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
+LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
+LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_jump (FuncState *fs);
+LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
+LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
+LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
+LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
+LUAI_FUNC int luaK_getlabel (FuncState *fs);
+LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);
+LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
+LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);
+LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
+
+
+#endif
diff --git a/engines/sword25/util/lua/ldblib.c b/engines/sword25/util/lua/ldblib.c
new file mode 100644
index 0000000000..67de1222a9
--- /dev/null
+++ b/engines/sword25/util/lua/ldblib.c
@@ -0,0 +1,397 @@
+/*
+** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static int db_getregistry (lua_State *L) {
+ lua_pushvalue(L, LUA_REGISTRYINDEX);
+ return 1;
+}
+
+
+static int db_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+}
+
+
+static int db_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
+ "nil or table expected");
+ lua_settop(L, 2);
+ lua_pushboolean(L, lua_setmetatable(L, 1));
+ return 1;
+}
+
+
+static int db_getfenv (lua_State *L) {
+ lua_getfenv(L, 1);
+ return 1;
+}
+
+
+static int db_setfenv (lua_State *L) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ lua_settop(L, 2);
+ if (lua_setfenv(L, 1) == 0)
+ luaL_error(L, LUA_QL("setfenv")
+ " cannot change environment of given object");
+ return 1;
+}
+
+
+static void settabss (lua_State *L, const char *i, const char *v) {
+ lua_pushstring(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static void settabsi (lua_State *L, const char *i, int v) {
+ lua_pushinteger(L, v);
+ lua_setfield(L, -2, i);
+}
+
+
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ }
+ else {
+ *arg = 0;
+ return L;
+ }
+}
+
+
+static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
+ if (L == L1) {
+ lua_pushvalue(L, -2);
+ lua_remove(L, -3);
+ }
+ else
+ lua_xmove(L1, L, 1);
+ lua_setfield(L, -2, fname);
+}
+
+
+static int db_getinfo (lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *options = luaL_optstring(L, arg+2, "flnSu");
+ if (lua_isnumber(L, arg+1)) {
+ if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
+ lua_pushnil(L); /* level out of range */
+ return 1;
+ }
+ }
+ else if (lua_isfunction(L, arg+1)) {
+ lua_pushfstring(L, ">%s", options);
+ options = lua_tostring(L, -1);
+ lua_pushvalue(L, arg+1);
+ lua_xmove(L, L1, 1);
+ }
+ else
+ return luaL_argerror(L, arg+1, "function or level expected");
+ if (!lua_getinfo(L1, options, &ar))
+ return luaL_argerror(L, arg+2, "invalid option");
+ lua_createtable(L, 0, 2);
+ if (strchr(options, 'S')) {
+ settabss(L, "source", ar.source);
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabsi(L, "lastlinedefined", ar.lastlinedefined);
+ settabss(L, "what", ar.what);
+ }
+ if (strchr(options, 'l'))
+ settabsi(L, "currentline", ar.currentline);
+ if (strchr(options, 'u'))
+ settabsi(L, "nups", ar.nups);
+ if (strchr(options, 'n')) {
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ }
+ if (strchr(options, 'L'))
+ treatstackoption(L, L1, "activelines");
+ if (strchr(options, 'f'))
+ treatstackoption(L, L1, "func");
+ return 1; /* return table */
+}
+
+
+static int db_getlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ const char *name;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
+ if (name) {
+ lua_xmove(L1, L, 1);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ return 2;
+ }
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int db_setlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ luaL_checkany(L, arg+3);
+ lua_settop(L, arg+3);
+ lua_xmove(L, L1, 1);
+ lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
+ return 1;
+}
+
+
+static int auxupvalue (lua_State *L, int get) {
+ const char *name;
+ int n = luaL_checkint(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
+ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+ if (name == NULL) return 0;
+ lua_pushstring(L, name);
+ lua_insert(L, -(get+1));
+ return get + 1;
+}
+
+
+static int db_getupvalue (lua_State *L) {
+ return auxupvalue(L, 1);
+}
+
+
+static int db_setupvalue (lua_State *L) {
+ luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+}
+
+
+
+static const char KEY_HOOK = 'h';
+
+
+static void hookf (lua_State *L, lua_Debug *ar) {
+ static const char *const hooknames[] =
+ {"call", "return", "line", "count", "tail return"};
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ lua_pushlightuserdata(L, L);
+ lua_rawget(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushstring(L, hooknames[(int)ar->event]);
+ if (ar->currentline >= 0)
+ lua_pushinteger(L, ar->currentline);
+ else lua_pushnil(L);
+ lua_assert(lua_getinfo(L, "lS", ar));
+ lua_call(L, 2, 0);
+ }
+}
+
+
+static int makemask (const char *smask, int count) {
+ int mask = 0;
+ if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
+ if (strchr(smask, 'r')) mask |= LUA_MASKRET;
+ if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
+ if (count > 0) mask |= LUA_MASKCOUNT;
+ return mask;
+}
+
+
+static char *unmakemask (int mask, char *smask) {
+ int i = 0;
+ if (mask & LUA_MASKCALL) smask[i++] = 'c';
+ if (mask & LUA_MASKRET) smask[i++] = 'r';
+ if (mask & LUA_MASKLINE) smask[i++] = 'l';
+ smask[i] = '\0';
+ return smask;
+}
+
+
+static void gethooktable (lua_State *L) {
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 1);
+ lua_pushlightuserdata(L, (void *)&KEY_HOOK);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+}
+
+
+static int db_sethook (lua_State *L) {
+ int arg, mask, count;
+ lua_Hook func;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_isnoneornil(L, arg+1)) {
+ lua_settop(L, arg+1);
+ func = NULL; mask = 0; count = 0; /* turn off hooks */
+ }
+ else {
+ const char *smask = luaL_checkstring(L, arg+2);
+ luaL_checktype(L, arg+1, LUA_TFUNCTION);
+ count = luaL_optint(L, arg+3, 0);
+ func = hookf; mask = makemask(smask, count);
+ }
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_pushvalue(L, arg+1);
+ lua_rawset(L, -3); /* set new hook */
+ lua_pop(L, 1); /* remove hook table */
+ lua_sethook(L1, func, mask, count); /* set hooks */
+ return 0;
+}
+
+
+static int db_gethook (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ char buff[5];
+ int mask = lua_gethookmask(L1);
+ lua_Hook hook = lua_gethook(L1);
+ if (hook != NULL && hook != hookf) /* external hook? */
+ lua_pushliteral(L, "external hook");
+ else {
+ gethooktable(L);
+ lua_pushlightuserdata(L, L1);
+ lua_rawget(L, -2); /* get hook */
+ lua_remove(L, -2); /* remove hook table */
+ }
+ lua_pushstring(L, unmakemask(mask, buff));
+ lua_pushinteger(L, lua_gethookcount(L1));
+ return 3;
+}
+
+
+static int db_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ fputs("lua_debug> ", stderr);
+ if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
+ lua_pcall(L, 0, 0, 0)) {
+ fputs(lua_tostring(L, -1), stderr);
+ fputs("\n", stderr);
+ }
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+#define LEVELS1 12 /* size of the first part of the stack */
+#define LEVELS2 10 /* size of the second part of the stack */
+
+static int db_errorfb (lua_State *L) {
+ int level;
+ int firstpart = 1; /* still before eventual `...' */
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ if (lua_isnumber(L, arg+2)) {
+ level = (int)lua_tointeger(L, arg+2);
+ lua_pop(L, 1);
+ }
+ else
+ level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
+ if (lua_gettop(L) == arg)
+ lua_pushliteral(L, "");
+ else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
+ else lua_pushliteral(L, "\n");
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level > LEVELS1 && firstpart) {
+ /* no more than `LEVELS2' more levels? */
+ if (!lua_getstack(L1, level+LEVELS2, &ar))
+ level--; /* keep going */
+ else {
+ lua_pushliteral(L, "\n\t..."); /* too many levels */
+ while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
+ level++;
+ }
+ firstpart = 0;
+ continue;
+ }
+ lua_pushliteral(L, "\n\t");
+ lua_getinfo(L1, "Snl", &ar);
+ lua_pushfstring(L, "%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ if (*ar.namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, " in function " LUA_QS, ar.name);
+ else {
+ if (*ar.what == 'm') /* main? */
+ lua_pushfstring(L, " in main chunk");
+ else if (*ar.what == 'C' || *ar.what == 't')
+ lua_pushliteral(L, " ?"); /* C function or tail call */
+ else
+ lua_pushfstring(L, " in function <%s:%d>",
+ ar.short_src, ar.linedefined);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ }
+ lua_concat(L, lua_gettop(L) - arg);
+ return 1;
+}
+
+
+static const luaL_Reg dblib[] = {
+ {"debug", db_debug},
+ {"getfenv", db_getfenv},
+ {"gethook", db_gethook},
+ {"getinfo", db_getinfo},
+ {"getlocal", db_getlocal},
+ {"getregistry", db_getregistry},
+ {"getmetatable", db_getmetatable},
+ {"getupvalue", db_getupvalue},
+ {"setfenv", db_setfenv},
+ {"sethook", db_sethook},
+ {"setlocal", db_setlocal},
+ {"setmetatable", db_setmetatable},
+ {"setupvalue", db_setupvalue},
+ {"traceback", db_errorfb},
+ {NULL, NULL}
+};
+
+
+LUALIB_API int luaopen_debug (lua_State *L) {
+ luaL_register(L, LUA_DBLIBNAME, dblib);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/ldebug.c b/engines/sword25/util/lua/ldebug.c
new file mode 100644
index 0000000000..9eac4a9b41
--- /dev/null
+++ b/engines/sword25/util/lua/ldebug.c
@@ -0,0 +1,622 @@
+/*
+** $Id: ldebug.c,v 2.29.1.3 2007/12/28 15:32:23 roberto Exp $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+
+#define ldebug_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
+
+
+static int currentpc (lua_State *L, CallInfo *ci) {
+ if (!isLua(ci)) return -1; /* function is not a Lua function? */
+ if (ci == L->ci)
+ ci->savedpc = L->savedpc;
+ return pcRel(ci->savedpc, ci_func(ci)->l.p);
+}
+
+
+static int currentline (lua_State *L, CallInfo *ci) {
+ int pc = currentpc(L, ci);
+ if (pc < 0)
+ return -1; /* only active lua functions have current-line information */
+ else
+ return getline(ci_func(ci)->l.p, pc);
+}
+
+
+/*
+** this function can be called asynchronous (e.g. during a signal)
+*/
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
+ if (func == NULL || mask == 0) { /* turn off hooks? */
+ mask = 0;
+ func = NULL;
+ }
+ L->hook = func;
+ L->basehookcount = count;
+ resethookcount(L);
+ L->hookmask = cast_byte(mask);
+ return 1;
+}
+
+
+LUA_API lua_Hook lua_gethook (lua_State *L) {
+ return L->hook;
+}
+
+
+LUA_API int lua_gethookmask (lua_State *L) {
+ return L->hookmask;
+}
+
+
+LUA_API int lua_gethookcount (lua_State *L) {
+ return L->basehookcount;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ int status;
+ CallInfo *ci;
+ lua_lock(L);
+ for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {
+ level--;
+ if (f_isLua(ci)) /* Lua function? */
+ level -= ci->tailcalls; /* skip lost tail calls */
+ }
+ if (level == 0 && ci > L->base_ci) { /* level found? */
+ status = 1;
+ ar->i_ci = cast_int(ci - L->base_ci);
+ }
+ else if (level < 0) { /* level is of a lost tail call? */
+ status = 1;
+ ar->i_ci = 0;
+ }
+ else status = 0; /* no such level */
+ lua_unlock(L);
+ return status;
+}
+
+
+static Proto *getluaproto (CallInfo *ci) {
+ return (isLua(ci) ? ci_func(ci)->l.p : NULL);
+}
+
+
+static const char *findlocal (lua_State *L, CallInfo *ci, int n) {
+ const char *name;
+ Proto *fp = getluaproto(ci);
+ if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)
+ return name; /* is a local variable in a Lua function */
+ else {
+ StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;
+ if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */
+ return "(*temporary)";
+ else
+ return NULL;
+ }
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ CallInfo *ci = L->base_ci + ar->i_ci;
+ const char *name = findlocal(L, ci, n);
+ lua_lock(L);
+ if (name)
+ luaA_pushobject(L, ci->base + (n - 1));
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ CallInfo *ci = L->base_ci + ar->i_ci;
+ const char *name = findlocal(L, ci, n);
+ lua_lock(L);
+ if (name)
+ setobjs2s(L, ci->base + (n - 1), L->top - 1);
+ L->top--; /* pop value */
+ lua_unlock(L);
+ return name;
+}
+
+
+static void funcinfo (lua_Debug *ar, Closure *cl) {
+ if (cl->c.isC) {
+ ar->source = "=[C]";
+ ar->linedefined = -1;
+ ar->lastlinedefined = -1;
+ ar->what = "C";
+ }
+ else {
+ ar->source = getstr(cl->l.p->source);
+ ar->linedefined = cl->l.p->linedefined;
+ ar->lastlinedefined = cl->l.p->lastlinedefined;
+ ar->what = (ar->linedefined == 0) ? "main" : "Lua";
+ }
+ luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
+}
+
+
+static void info_tailcall (lua_Debug *ar) {
+ ar->name = ar->namewhat = "";
+ ar->what = "tail";
+ ar->lastlinedefined = ar->linedefined = ar->currentline = -1;
+ ar->source = "=(tail call)";
+ luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
+ ar->nups = 0;
+}
+
+
+static void collectvalidlines (lua_State *L, Closure *f) {
+ if (f == NULL || f->c.isC) {
+ setnilvalue(L->top);
+ }
+ else {
+ Table *t = luaH_new(L, 0, 0);
+ int *lineinfo = f->l.p->lineinfo;
+ int i;
+ for (i=0; i<f->l.p->sizelineinfo; i++)
+ setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);
+ sethvalue(L, L->top, t);
+ }
+ incr_top(L);
+}
+
+
+static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
+ Closure *f, CallInfo *ci) {
+ int status = 1;
+ if (f == NULL) {
+ info_tailcall(ar);
+ return status;
+ }
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(ar, f);
+ break;
+ }
+ case 'l': {
+ ar->currentline = (ci) ? currentline(L, ci) : -1;
+ break;
+ }
+ case 'u': {
+ ar->nups = f->c.nupvalues;
+ break;
+ }
+ case 'n': {
+ ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;
+ if (ar->namewhat == NULL) {
+ ar->namewhat = ""; /* not found */
+ ar->name = NULL;
+ }
+ break;
+ }
+ case 'L':
+ case 'f': /* handled by lua_getinfo */
+ break;
+ default: status = 0; /* invalid option */
+ }
+ }
+ return status;
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ int status;
+ Closure *f = NULL;
+ CallInfo *ci = NULL;
+ lua_lock(L);
+ if (*what == '>') {
+ StkId func = L->top - 1;
+ luai_apicheck(L, ttisfunction(func));
+ what++; /* skip the '>' */
+ f = clvalue(func);
+ L->top--; /* pop function */
+ }
+ else if (ar->i_ci != 0) { /* no tail call? */
+ ci = L->base_ci + ar->i_ci;
+ lua_assert(ttisfunction(ci->func));
+ f = clvalue(ci->func);
+ }
+ status = auxgetinfo(L, what, ar, f, ci);
+ if (strchr(what, 'f')) {
+ if (f == NULL) setnilvalue(L->top);
+ else setclvalue(L, L->top, f);
+ incr_top(L);
+ }
+ if (strchr(what, 'L'))
+ collectvalidlines(L, f);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution and code checker
+** =======================================================
+*/
+
+#define check(x) if (!(x)) return 0;
+
+#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode)
+
+#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize)
+
+
+
+static int precheck (const Proto *pt) {
+ check(pt->maxstacksize <= MAXSTACK);
+ lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
+ lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) ||
+ (pt->is_vararg & VARARG_HASARG));
+ check(pt->sizeupvalues <= pt->nups);
+ check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
+ check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
+ return 1;
+}
+
+
+#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1])
+
+int luaG_checkopenop (Instruction i) {
+ switch (GET_OPCODE(i)) {
+ case OP_CALL:
+ case OP_TAILCALL:
+ case OP_RETURN:
+ case OP_SETLIST: {
+ check(GETARG_B(i) == 0);
+ return 1;
+ }
+ default: return 0; /* invalid instruction after an open call */
+ }
+}
+
+
+static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {
+ switch (mode) {
+ case OpArgN: check(r == 0); break;
+ case OpArgU: break;
+ case OpArgR: checkreg(pt, r); break;
+ case OpArgK:
+ check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);
+ break;
+ }
+ return 1;
+}
+
+
+static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
+ int pc;
+ int last; /* stores position of last instruction that changed `reg' */
+ last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
+ check(precheck(pt));
+ for (pc = 0; pc < lastpc; pc++) {
+ Instruction i = pt->code[pc];
+ OpCode op = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int b = 0;
+ int c = 0;
+ check(op < NUM_OPCODES);
+ checkreg(pt, a);
+ switch (getOpMode(op)) {
+ case iABC: {
+ b = GETARG_B(i);
+ c = GETARG_C(i);
+ check(checkArgMode(pt, b, getBMode(op)));
+ check(checkArgMode(pt, c, getCMode(op)));
+ break;
+ }
+ case iABx: {
+ b = GETARG_Bx(i);
+ if (getBMode(op) == OpArgK) check(b < pt->sizek);
+ break;
+ }
+ case iAsBx: {
+ b = GETARG_sBx(i);
+ if (getBMode(op) == OpArgR) {
+ int dest = pc+1+b;
+ check(0 <= dest && dest < pt->sizecode);
+ if (dest > 0) {
+ /* cannot jump to a setlist count */
+ Instruction d = pt->code[dest-1];
+ check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0));
+ }
+ }
+ break;
+ }
+ }
+ if (testAMode(op)) {
+ if (a == reg) last = pc; /* change register `a' */
+ }
+ if (testTMode(op)) {
+ check(pc+2 < pt->sizecode); /* check skip */
+ check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);
+ }
+ switch (op) {
+ case OP_LOADBOOL: {
+ check(c == 0 || pc+2 < pt->sizecode); /* check its jump */
+ break;
+ }
+ case OP_LOADNIL: {
+ if (a <= reg && reg <= b)
+ last = pc; /* set registers from `a' to `b' */
+ break;
+ }
+ case OP_GETUPVAL:
+ case OP_SETUPVAL: {
+ check(b < pt->nups);
+ break;
+ }
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL: {
+ check(ttisstring(&pt->k[b]));
+ break;
+ }
+ case OP_SELF: {
+ checkreg(pt, a+1);
+ if (reg == a+1) last = pc;
+ break;
+ }
+ case OP_CONCAT: {
+ check(b < c); /* at least two operands */
+ break;
+ }
+ case OP_TFORLOOP: {
+ check(c >= 1); /* at least one result (control variable) */
+ checkreg(pt, a+2+c); /* space for results */
+ if (reg >= a+2) last = pc; /* affect all regs above its base */
+ break;
+ }
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ checkreg(pt, a+3);
+ /* go through */
+ case OP_JMP: {
+ int dest = pc+1+b;
+ /* not full check and jump is forward and do not skip `lastpc'? */
+ if (reg != NO_REG && pc < dest && dest <= lastpc)
+ pc += b; /* do the jump */
+ break;
+ }
+ case OP_CALL:
+ case OP_TAILCALL: {
+ if (b != 0) {
+ checkreg(pt, a+b-1);
+ }
+ c--; /* c = num. returns */
+ if (c == LUA_MULTRET) {
+ check(checkopenop(pt, pc));
+ }
+ else if (c != 0)
+ checkreg(pt, a+c-1);
+ if (reg >= a) last = pc; /* affect all registers above base */
+ break;
+ }
+ case OP_RETURN: {
+ b--; /* b = num. returns */
+ if (b > 0) checkreg(pt, a+b-1);
+ break;
+ }
+ case OP_SETLIST: {
+ if (b > 0) checkreg(pt, a + b);
+ if (c == 0) pc++;
+ break;
+ }
+ case OP_CLOSURE: {
+ int nup, j;
+ check(b < pt->sizep);
+ nup = pt->p[b]->nups;
+ check(pc + nup < pt->sizecode);
+ for (j = 1; j <= nup; j++) {
+ OpCode op1 = GET_OPCODE(pt->code[pc + j]);
+ check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
+ }
+ if (reg != NO_REG) /* tracing? */
+ pc += nup; /* do not 'execute' these pseudo-instructions */
+ break;
+ }
+ case OP_VARARG: {
+ check((pt->is_vararg & VARARG_ISVARARG) &&
+ !(pt->is_vararg & VARARG_NEEDSARG));
+ b--;
+ if (b == LUA_MULTRET) check(checkopenop(pt, pc));
+ checkreg(pt, a+b-1);
+ break;
+ }
+ default: break;
+ }
+ }
+ return pt->code[last];
+}
+
+#undef check
+#undef checkjump
+#undef checkreg
+
+/* }====================================================== */
+
+
+int luaG_checkcode (const Proto *pt) {
+ return (symbexec(pt, pt->sizecode, NO_REG) != 0);
+}
+
+
+static const char *kname (Proto *p, int c) {
+ if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))
+ return svalue(&p->k[INDEXK(c)]);
+ else
+ return "?";
+}
+
+
+static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,
+ const char **name) {
+ if (isLua(ci)) { /* a Lua function? */
+ Proto *p = ci_func(ci)->l.p;
+ int pc = currentpc(L, ci);
+ Instruction i;
+ *name = luaF_getlocalname(p, stackpos+1, pc);
+ if (*name) /* is a local? */
+ return "local";
+ i = symbexec(p, pc, stackpos); /* try symbolic execution */
+ lua_assert(pc != -1);
+ switch (GET_OPCODE(i)) {
+ case OP_GETGLOBAL: {
+ int g = GETARG_Bx(i); /* global index */
+ lua_assert(ttisstring(&p->k[g]));
+ *name = svalue(&p->k[g]);
+ return "global";
+ }
+ case OP_MOVE: {
+ int a = GETARG_A(i);
+ int b = GETARG_B(i); /* move from `b' to `a' */
+ if (b < a)
+ return getobjname(L, ci, b, name); /* get name for `b' */
+ break;
+ }
+ case OP_GETTABLE: {
+ int k = GETARG_C(i); /* key index */
+ *name = kname(p, k);
+ return "field";
+ }
+ case OP_GETUPVAL: {
+ int u = GETARG_B(i); /* upvalue index */
+ *name = p->upvalues ? getstr(p->upvalues[u]) : "?";
+ return "upvalue";
+ }
+ case OP_SELF: {
+ int k = GETARG_C(i); /* key index */
+ *name = kname(p, k);
+ return "method";
+ }
+ default: break;
+ }
+ }
+ return NULL; /* no useful name found */
+}
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
+ Instruction i;
+ if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))
+ return NULL; /* calling function is not Lua (or is unknown) */
+ ci--; /* calling function */
+ i = ci_func(ci)->l.p->code[currentpc(L, ci)];
+ if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||
+ GET_OPCODE(i) == OP_TFORLOOP)
+ return getobjname(L, ci, GETARG_A(i), name);
+ else
+ return NULL; /* no useful name can be found */
+}
+
+
+/* only ANSI way to check whether a pointer points to an array */
+static int isinstack (CallInfo *ci, const TValue *o) {
+ StkId p;
+ for (p = ci->base; p < ci->top; p++)
+ if (o == p) return 1;
+ return 0;
+}
+
+
+void luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
+ const char *name = NULL;
+ const char *t = luaT_typenames[ttype(o)];
+ const char *kind = (isinstack(L->ci, o)) ?
+ getobjname(L, L->ci, cast_int(o - L->base), &name) :
+ NULL;
+ if (kind)
+ luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)",
+ op, kind, name, t);
+ else
+ luaG_runerror(L, "attempt to %s a %s value", op, t);
+}
+
+
+void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
+ if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
+ lua_assert(!ttisstring(p1) && !ttisnumber(p1));
+ luaG_typeerror(L, p1, "concatenate");
+}
+
+
+void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
+ TValue temp;
+ if (luaV_tonumber(p1, &temp) == NULL)
+ p2 = p1; /* first operand is wrong */
+ luaG_typeerror(L, p2, "perform arithmetic on");
+}
+
+
+int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
+ const char *t1 = luaT_typenames[ttype(p1)];
+ const char *t2 = luaT_typenames[ttype(p2)];
+ if (t1[2] == t2[2])
+ luaG_runerror(L, "attempt to compare two %s values", t1);
+ else
+ luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
+ return 0;
+}
+
+
+static void addinfo (lua_State *L, const char *msg) {
+ CallInfo *ci = L->ci;
+ if (isLua(ci)) { /* is Lua code? */
+ char buff[LUA_IDSIZE]; /* add file:line information */
+ int line = currentline(L, ci);
+ luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);
+ luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
+ }
+}
+
+
+void luaG_errormsg (lua_State *L) {
+ if (L->errfunc != 0) { /* is there an error handling function? */
+ StkId errfunc = restorestack(L, L->errfunc);
+ if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
+ setobjs2s(L, L->top, L->top - 1); /* move argument */
+ setobjs2s(L, L->top - 1, errfunc); /* push function */
+ incr_top(L);
+ luaD_call(L, L->top - 2, 1); /* call it */
+ }
+ luaD_throw(L, LUA_ERRRUN);
+}
+
+
+void luaG_runerror (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ addinfo(L, luaO_pushvfstring(L, fmt, argp));
+ va_end(argp);
+ luaG_errormsg(L);
+}
+
diff --git a/engines/sword25/util/lua/ldebug.h b/engines/sword25/util/lua/ldebug.h
new file mode 100644
index 0000000000..ba28a97248
--- /dev/null
+++ b/engines/sword25/util/lua/ldebug.h
@@ -0,0 +1,33 @@
+/*
+** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+
+
+#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
+
+#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
+
+#define resethookcount(L) (L->hookcount = L->basehookcount)
+
+
+LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,
+ const char *opname);
+LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);
+LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaG_errormsg (lua_State *L);
+LUAI_FUNC int luaG_checkcode (const Proto *pt);
+LUAI_FUNC int luaG_checkopenop (Instruction i);
+
+#endif
diff --git a/engines/sword25/util/lua/ldo.c b/engines/sword25/util/lua/ldo.c
new file mode 100644
index 0000000000..8de05f728e
--- /dev/null
+++ b/engines/sword25/util/lua/ldo.c
@@ -0,0 +1,518 @@
+/*
+** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define ldo_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+
+
+/*
+** {======================================================
+** Error-recovery functions
+** =======================================================
+*/
+
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ struct lua_longjmp *previous;
+ luai_jmpbuf b;
+ volatile int status; /* error code */
+};
+
+
+void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
+ switch (errcode) {
+ case LUA_ERRMEM: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
+ break;
+ }
+ case LUA_ERRERR: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
+ break;
+ }
+ case LUA_ERRSYNTAX:
+ case LUA_ERRRUN: {
+ setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
+ break;
+ }
+ }
+ L->top = oldtop + 1;
+}
+
+
+static void restore_stack_limit (lua_State *L) {
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
+ int inuse = cast_int(L->ci - L->base_ci);
+ if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
+ luaD_reallocCI(L, LUAI_MAXCALLS);
+ }
+}
+
+
+static void resetstack (lua_State *L, int status) {
+ L->ci = L->base_ci;
+ L->base = L->ci->base;
+ luaF_close(L, L->base); /* close eventual pending closures */
+ luaD_seterrorobj(L, status, L->base);
+ L->nCcalls = L->baseCcalls;
+ L->allowhook = 1;
+ restore_stack_limit(L);
+ L->errfunc = 0;
+ L->errorJmp = NULL;
+}
+
+
+void luaD_throw (lua_State *L, int errcode) {
+ if (L->errorJmp) {
+ L->errorJmp->status = errcode;
+ LUAI_THROW(L, L->errorJmp);
+ }
+ else {
+ L->status = cast_byte(errcode);
+ if (G(L)->panic) {
+ resetstack(L, errcode);
+ lua_unlock(L);
+ G(L)->panic(L);
+ }
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
+ struct lua_longjmp lj;
+ lj.status = 0;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ LUAI_TRY(L, &lj,
+ (*f)(L, ud);
+ );
+ L->errorJmp = lj.previous; /* restore old error handler */
+ return lj.status;
+}
+
+/* }====================================================== */
+
+
+static void correctstack (lua_State *L, TValue *oldstack) {
+ CallInfo *ci;
+ GCObject *up;
+ L->top = (L->top - oldstack) + L->stack;
+ for (up = L->openupval; up != NULL; up = up->gch.next)
+ gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
+ for (ci = L->base_ci; ci <= L->ci; ci++) {
+ ci->top = (ci->top - oldstack) + L->stack;
+ ci->base = (ci->base - oldstack) + L->stack;
+ ci->func = (ci->func - oldstack) + L->stack;
+ }
+ L->base = (L->base - oldstack) + L->stack;
+}
+
+
+void luaD_reallocstack (lua_State *L, int newsize) {
+ TValue *oldstack = L->stack;
+ int realsize = newsize + 1 + EXTRA_STACK;
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
+ L->stacksize = realsize;
+ L->stack_last = L->stack+newsize;
+ correctstack(L, oldstack);
+}
+
+
+void luaD_reallocCI (lua_State *L, int newsize) {
+ CallInfo *oldci = L->base_ci;
+ luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
+ L->size_ci = newsize;
+ L->ci = (L->ci - oldci) + L->base_ci;
+ L->end_ci = L->base_ci + L->size_ci - 1;
+}
+
+
+void luaD_growstack (lua_State *L, int n) {
+ if (n <= L->stacksize) /* double size is enough? */
+ luaD_reallocstack(L, 2*L->stacksize);
+ else
+ luaD_reallocstack(L, L->stacksize + n);
+}
+
+
+static CallInfo *growCI (lua_State *L) {
+ if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */
+ luaD_throw(L, LUA_ERRERR);
+ else {
+ luaD_reallocCI(L, 2*L->size_ci);
+ if (L->size_ci > LUAI_MAXCALLS)
+ luaG_runerror(L, "stack overflow");
+ }
+ return ++L->ci;
+}
+
+
+void luaD_callhook (lua_State *L, int event, int line) {
+ lua_Hook hook = L->hook;
+ if (hook && L->allowhook) {
+ ptrdiff_t top = savestack(L, L->top);
+ ptrdiff_t ci_top = savestack(L, L->ci->top);
+ lua_Debug ar;
+ ar.event = event;
+ ar.currentline = line;
+ if (event == LUA_HOOKTAILRET)
+ ar.i_ci = 0; /* tail call; no debug information about it */
+ else
+ ar.i_ci = cast_int(L->ci - L->base_ci);
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ L->ci->top = L->top + LUA_MINSTACK;
+ lua_assert(L->ci->top <= L->stack_last);
+ L->allowhook = 0; /* cannot call hooks inside a hook */
+ lua_unlock(L);
+ (*hook)(L, &ar);
+ lua_lock(L);
+ lua_assert(!L->allowhook);
+ L->allowhook = 1;
+ L->ci->top = restorestack(L, ci_top);
+ L->top = restorestack(L, top);
+ }
+}
+
+
+static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
+ int i;
+ int nfixargs = p->numparams;
+ Table *htab = NULL;
+ StkId base, fixed;
+ for (; actual < nfixargs; ++actual)
+ setnilvalue(L->top++);
+#if defined(LUA_COMPAT_VARARG)
+ if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
+ int nvar = actual - nfixargs; /* number of extra arguments */
+ lua_assert(p->is_vararg & VARARG_HASARG);
+ luaC_checkGC(L);
+ htab = luaH_new(L, nvar, 1); /* create `arg' table */
+ for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
+ setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
+ /* store counter in field `n' */
+ setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
+ }
+#endif
+ /* move fixed parameters to final position */
+ fixed = L->top - actual; /* first fixed argument */
+ base = L->top; /* final position of first argument */
+ for (i=0; i<nfixargs; i++) {
+ setobjs2s(L, L->top++, fixed+i);
+ setnilvalue(fixed+i);
+ }
+ /* add `arg' parameter */
+ if (htab) {
+ sethvalue(L, L->top++, htab);
+ lua_assert(iswhite(obj2gco(htab)));
+ }
+ return base;
+}
+
+
+static StkId tryfuncTM (lua_State *L, StkId func) {
+ const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
+ StkId p;
+ ptrdiff_t funcr = savestack(L, func);
+ if (!ttisfunction(tm))
+ luaG_typeerror(L, func, "call");
+ /* Open a hole inside the stack at `func' */
+ for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
+ incr_top(L);
+ func = restorestack(L, funcr); /* previous call may change stack */
+ setobj2s(L, func, tm); /* tag method is the new function to be called */
+ return func;
+}
+
+
+
+#define inc_ci(L) \
+ ((L->ci == L->end_ci) ? growCI(L) : \
+ (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))
+
+
+int luaD_precall (lua_State *L, StkId func, int nresults) {
+ LClosure *cl;
+ ptrdiff_t funcr;
+ if (!ttisfunction(func)) /* `func' is not a function? */
+ func = tryfuncTM(L, func); /* check the `function' tag method */
+ funcr = savestack(L, func);
+ cl = &clvalue(func)->l;
+ L->ci->savedpc = L->savedpc;
+ if (!cl->isC) { /* Lua function? prepare its call */
+ CallInfo *ci;
+ StkId st, base;
+ Proto *p = cl->p;
+ luaD_checkstack(L, p->maxstacksize);
+ func = restorestack(L, funcr);
+ if (!p->is_vararg) { /* no varargs? */
+ base = func + 1;
+ if (L->top > base + p->numparams)
+ L->top = base + p->numparams;
+ }
+ else { /* vararg function */
+ int nargs = cast_int(L->top - func) - 1;
+ base = adjust_varargs(L, p, nargs);
+ func = restorestack(L, funcr); /* previous call may change the stack */
+ }
+ ci = inc_ci(L); /* now `enter' new function */
+ ci->func = func;
+ L->base = ci->base = base;
+ ci->top = L->base + p->maxstacksize;
+ lua_assert(ci->top <= L->stack_last);
+ L->savedpc = p->code; /* starting point */
+ ci->tailcalls = 0;
+ ci->nresults = nresults;
+ for (st = L->top; st < ci->top; st++)
+ setnilvalue(st);
+ L->top = ci->top;
+ if (L->hookmask & LUA_MASKCALL) {
+ L->savedpc++; /* hooks assume 'pc' is already incremented */
+ luaD_callhook(L, LUA_HOOKCALL, -1);
+ L->savedpc--; /* correct 'pc' */
+ }
+ return PCRLUA;
+ }
+ else { /* if is a C function, call it */
+ CallInfo *ci;
+ int n;
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ ci = inc_ci(L); /* now `enter' new function */
+ ci->func = restorestack(L, funcr);
+ L->base = ci->base = ci->func + 1;
+ ci->top = L->top + LUA_MINSTACK;
+ lua_assert(ci->top <= L->stack_last);
+ ci->nresults = nresults;
+ if (L->hookmask & LUA_MASKCALL)
+ luaD_callhook(L, LUA_HOOKCALL, -1);
+ lua_unlock(L);
+ n = (*curr_func(L)->c.f)(L); /* do the actual call */
+ lua_lock(L);
+ if (n < 0) /* yielding? */
+ return PCRYIELD;
+ else {
+ luaD_poscall(L, L->top - n);
+ return PCRC;
+ }
+ }
+}
+
+
+static StkId callrethooks (lua_State *L, StkId firstResult) {
+ ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */
+ luaD_callhook(L, LUA_HOOKRET, -1);
+ if (f_isLua(L->ci)) { /* Lua function? */
+ while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */
+ luaD_callhook(L, LUA_HOOKTAILRET, -1);
+ }
+ return restorestack(L, fr);
+}
+
+
+int luaD_poscall (lua_State *L, StkId firstResult) {
+ StkId res;
+ int wanted, i;
+ CallInfo *ci;
+ if (L->hookmask & LUA_MASKRET)
+ firstResult = callrethooks(L, firstResult);
+ ci = L->ci--;
+ res = ci->func; /* res == final position of 1st result */
+ wanted = ci->nresults;
+ L->base = (ci - 1)->base; /* restore base */
+ L->savedpc = (ci - 1)->savedpc; /* restore savedpc */
+ /* move results to correct place */
+ for (i = wanted; i != 0 && firstResult < L->top; i--)
+ setobjs2s(L, res++, firstResult++);
+ while (i-- > 0)
+ setnilvalue(res++);
+ L->top = res;
+ return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
+}
+
+
+/*
+** Call a function (C or Lua). The function to be called is at *func.
+** The arguments are on the stack, right after the function.
+** When returns, all the results are on the stack, starting at the original
+** function position.
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ if (++L->nCcalls >= LUAI_MAXCCALLS) {
+ if (L->nCcalls == LUAI_MAXCCALLS)
+ luaG_runerror(L, "C stack overflow");
+ else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
+ luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
+ }
+ if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
+ luaV_execute(L, 1); /* call it */
+ L->nCcalls--;
+ luaC_checkGC(L);
+}
+
+
+static void resume (lua_State *L, void *ud) {
+ StkId firstArg = cast(StkId, ud);
+ CallInfo *ci = L->ci;
+ if (L->status == 0) { /* start coroutine? */
+ lua_assert(ci == L->base_ci && firstArg > L->base);
+ if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)
+ return;
+ }
+ else { /* resuming from previous yield */
+ lua_assert(L->status == LUA_YIELD);
+ L->status = 0;
+ if (!f_isLua(ci)) { /* `common' yield? */
+ /* finish interrupted execution of `OP_CALL' */
+ lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
+ GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);
+ if (luaD_poscall(L, firstArg)) /* complete it... */
+ L->top = L->ci->top; /* and correct top if not multiple results */
+ }
+ else /* yielded inside a hook: just continue its execution */
+ L->base = L->ci->base;
+ }
+ luaV_execute(L, cast_int(L->ci - L->base_ci));
+}
+
+
+static int resume_error (lua_State *L, const char *msg) {
+ L->top = L->ci->base;
+ setsvalue2s(L, L->top, luaS_new(L, msg));
+ incr_top(L);
+ lua_unlock(L);
+ return LUA_ERRRUN;
+}
+
+
+LUA_API int lua_resume (lua_State *L, int nargs) {
+ int status;
+ lua_lock(L);
+ if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))
+ return resume_error(L, "cannot resume non-suspended coroutine");
+ if (L->nCcalls >= LUAI_MAXCCALLS)
+ return resume_error(L, "C stack overflow");
+ luai_userstateresume(L, nargs);
+ lua_assert(L->errfunc == 0);
+ L->baseCcalls = ++L->nCcalls;
+ status = luaD_rawrunprotected(L, resume, L->top - nargs);
+ if (status != 0) { /* error? */
+ L->status = cast_byte(status); /* mark thread as `dead' */
+ luaD_seterrorobj(L, status, L->top);
+ L->ci->top = L->top;
+ }
+ else {
+ lua_assert(L->nCcalls == L->baseCcalls);
+ status = L->status;
+ }
+ --L->nCcalls;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_yield (lua_State *L, int nresults) {
+ luai_userstateyield(L, nresults);
+ lua_lock(L);
+ if (L->nCcalls > L->baseCcalls)
+ luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
+ L->base = L->top - nresults; /* protect stack slots below */
+ L->status = LUA_YIELD;
+ lua_unlock(L);
+ return -1;
+}
+
+
+int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t old_top, ptrdiff_t ef) {
+ int status;
+ unsigned short oldnCcalls = L->nCcalls;
+ ptrdiff_t old_ci = saveci(L, L->ci);
+ lu_byte old_allowhooks = L->allowhook;
+ ptrdiff_t old_errfunc = L->errfunc;
+ L->errfunc = ef;
+ status = luaD_rawrunprotected(L, func, u);
+ if (status != 0) { /* an error occurred? */
+ StkId oldtop = restorestack(L, old_top);
+ luaF_close(L, oldtop); /* close eventual pending closures */
+ luaD_seterrorobj(L, status, oldtop);
+ L->nCcalls = oldnCcalls;
+ L->ci = restoreci(L, old_ci);
+ L->base = L->ci->base;
+ L->savedpc = L->ci->savedpc;
+ L->allowhook = old_allowhooks;
+ restore_stack_limit(L);
+ }
+ L->errfunc = old_errfunc;
+ return status;
+}
+
+
+
+/*
+** Execute a protected parser.
+*/
+struct SParser { /* data to `f_parser' */
+ ZIO *z;
+ Mbuffer buff; /* buffer to be used by the scanner */
+ const char *name;
+};
+
+static void f_parser (lua_State *L, void *ud) {
+ int i;
+ Proto *tf;
+ Closure *cl;
+ struct SParser *p = cast(struct SParser *, ud);
+ int c = luaZ_lookahead(p->z);
+ luaC_checkGC(L);
+ tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
+ &p->buff, p->name);
+ cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
+ cl->l.p = tf;
+ for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
+ cl->l.upvals[i] = luaF_newupval(L);
+ setclvalue(L, L->top, cl);
+ incr_top(L);
+}
+
+
+int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
+ struct SParser p;
+ int status;
+ p.z = z; p.name = name;
+ luaZ_initbuffer(L, &p.buff);
+ status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
+ luaZ_freebuffer(L, &p.buff);
+ return status;
+}
+
+
diff --git a/engines/sword25/util/lua/ldo.h b/engines/sword25/util/lua/ldo.h
new file mode 100644
index 0000000000..98fddac59f
--- /dev/null
+++ b/engines/sword25/util/lua/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+#define luaD_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ luaD_growstack(L, n); \
+ else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
+
+
+#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
+
+#define savestack(L,p) ((char *)(p) - (char *)L->stack)
+#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
+
+#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
+#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
+
+
+/* results from luaD_precall */
+#define PCRLUA 0 /* initiated a call to a Lua function */
+#define PCRC 1 /* did a call to a C function */
+#define PCRYIELD 2 /* C funtion yielded */
+
+
+/* type of protected functions, to be ran by `runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
+LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
+LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
+LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
+LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
+LUAI_FUNC void luaD_growstack (lua_State *L, int n);
+
+LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+
+#endif
+
diff --git a/engines/sword25/util/lua/ldump.c b/engines/sword25/util/lua/ldump.c
new file mode 100644
index 0000000000..c9d3d4870f
--- /dev/null
+++ b/engines/sword25/util/lua/ldump.c
@@ -0,0 +1,164 @@
+/*
+** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** save precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#include <stddef.h>
+
+#define ldump_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lundump.h"
+
+typedef struct {
+ lua_State* L;
+ lua_Writer writer;
+ void* data;
+ int strip;
+ int status;
+} DumpState;
+
+#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D)
+#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D)
+
+static void DumpBlock(const void* b, size_t size, DumpState* D)
+{
+ if (D->status==0)
+ {
+ lua_unlock(D->L);
+ D->status=(*D->writer)(D->L,b,size,D->data);
+ lua_lock(D->L);
+ }
+}
+
+static void DumpChar(int y, DumpState* D)
+{
+ char x=(char)y;
+ DumpVar(x,D);
+}
+
+static void DumpInt(int x, DumpState* D)
+{
+ DumpVar(x,D);
+}
+
+static void DumpNumber(lua_Number x, DumpState* D)
+{
+ DumpVar(x,D);
+}
+
+static void DumpVector(const void* b, int n, size_t size, DumpState* D)
+{
+ DumpInt(n,D);
+ DumpMem(b,n,size,D);
+}
+
+static void DumpString(const TString* s, DumpState* D)
+{
+ if (s==NULL || getstr(s)==NULL)
+ {
+ size_t size=0;
+ DumpVar(size,D);
+ }
+ else
+ {
+ size_t size=s->tsv.len+1; /* include trailing '\0' */
+ DumpVar(size,D);
+ DumpBlock(getstr(s),size,D);
+ }
+}
+
+#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D)
+
+static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
+
+static void DumpConstants(const Proto* f, DumpState* D)
+{
+ int i,n=f->sizek;
+ DumpInt(n,D);
+ for (i=0; i<n; i++)
+ {
+ const TValue* o=&f->k[i];
+ DumpChar(ttype(o),D);
+ switch (ttype(o))
+ {
+ case LUA_TNIL:
+ break;
+ case LUA_TBOOLEAN:
+ DumpChar(bvalue(o),D);
+ break;
+ case LUA_TNUMBER:
+ DumpNumber(nvalue(o),D);
+ break;
+ case LUA_TSTRING:
+ DumpString(rawtsvalue(o),D);
+ break;
+ default:
+ lua_assert(0); /* cannot happen */
+ break;
+ }
+ }
+ n=f->sizep;
+ DumpInt(n,D);
+ for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
+}
+
+static void DumpDebug(const Proto* f, DumpState* D)
+{
+ int i,n;
+ n= (D->strip) ? 0 : f->sizelineinfo;
+ DumpVector(f->lineinfo,n,sizeof(int),D);
+ n= (D->strip) ? 0 : f->sizelocvars;
+ DumpInt(n,D);
+ for (i=0; i<n; i++)
+ {
+ DumpString(f->locvars[i].varname,D);
+ DumpInt(f->locvars[i].startpc,D);
+ DumpInt(f->locvars[i].endpc,D);
+ }
+ n= (D->strip) ? 0 : f->sizeupvalues;
+ DumpInt(n,D);
+ for (i=0; i<n; i++) DumpString(f->upvalues[i],D);
+}
+
+static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
+{
+ DumpString((f->source==p || D->strip) ? NULL : f->source,D);
+ DumpInt(f->linedefined,D);
+ DumpInt(f->lastlinedefined,D);
+ DumpChar(f->nups,D);
+ DumpChar(f->numparams,D);
+ DumpChar(f->is_vararg,D);
+ DumpChar(f->maxstacksize,D);
+ DumpCode(f,D);
+ DumpConstants(f,D);
+ DumpDebug(f,D);
+}
+
+static void DumpHeader(DumpState* D)
+{
+ char h[LUAC_HEADERSIZE];
+ luaU_header(h);
+ DumpBlock(h,LUAC_HEADERSIZE,D);
+}
+
+/*
+** dump Lua function as precompiled chunk
+*/
+int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
+{
+ DumpState D;
+ D.L=L;
+ D.writer=w;
+ D.data=data;
+ D.strip=strip;
+ D.status=0;
+ DumpHeader(&D);
+ DumpFunction(f,NULL,&D);
+ return D.status;
+}
diff --git a/engines/sword25/util/lua/lfunc.c b/engines/sword25/util/lua/lfunc.c
new file mode 100644
index 0000000000..813e88f583
--- /dev/null
+++ b/engines/sword25/util/lua/lfunc.c
@@ -0,0 +1,174 @@
+/*
+** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lfunc_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));
+ luaC_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->c.isC = 1;
+ c->c.env = e;
+ c->c.nupvalues = cast_byte(nelems);
+ return c;
+}
+
+
+Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));
+ luaC_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->l.isC = 0;
+ c->l.env = e;
+ c->l.nupvalues = cast_byte(nelems);
+ while (nelems--) c->l.upvals[nelems] = NULL;
+ return c;
+}
+
+
+UpVal *luaF_newupval (lua_State *L) {
+ UpVal *uv = luaM_new(L, UpVal);
+ luaC_link(L, obj2gco(uv), LUA_TUPVAL);
+ uv->v = &uv->u.value;
+ setnilvalue(uv->v);
+ return uv;
+}
+
+
+UpVal *luaF_findupval (lua_State *L, StkId level) {
+ global_State *g = G(L);
+ GCObject **pp = &L->openupval;
+ UpVal *p;
+ UpVal *uv;
+ while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {
+ lua_assert(p->v != &p->u.value);
+ if (p->v == level) { /* found a corresponding upvalue? */
+ if (isdead(g, obj2gco(p))) /* is it dead? */
+ changewhite(obj2gco(p)); /* ressurect it */
+ return p;
+ }
+ pp = &p->next;
+ }
+ uv = luaM_new(L, UpVal); /* not found: create a new one */
+ uv->tt = LUA_TUPVAL;
+ uv->marked = luaC_white(g);
+ uv->v = level; /* current value lives in the stack */
+ uv->next = *pp; /* chain it in the proper position */
+ *pp = obj2gco(uv);
+ uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
+ uv->u.l.next = g->uvhead.u.l.next;
+ uv->u.l.next->u.l.prev = uv;
+ g->uvhead.u.l.next = uv;
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ return uv;
+}
+
+
+static void unlinkupval (UpVal *uv) {
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */
+ uv->u.l.prev->u.l.next = uv->u.l.next;
+}
+
+
+void luaF_freeupval (lua_State *L, UpVal *uv) {
+ if (uv->v != &uv->u.value) /* is it open? */
+ unlinkupval(uv); /* remove from open list */
+ luaM_free(L, uv); /* free upvalue */
+}
+
+
+void luaF_close (lua_State *L, StkId level) {
+ UpVal *uv;
+ global_State *g = G(L);
+ while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {
+ GCObject *o = obj2gco(uv);
+ lua_assert(!isblack(o) && uv->v != &uv->u.value);
+ L->openupval = uv->next; /* remove from `open' list */
+ if (isdead(g, o))
+ luaF_freeupval(L, uv); /* free upvalue */
+ else {
+ unlinkupval(uv);
+ setobj(L, &uv->u.value, uv->v);
+ uv->v = &uv->u.value; /* now current value lives here */
+ luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
+ }
+ }
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ Proto *f = luaM_new(L, Proto);
+ luaC_link(L, obj2gco(f), LUA_TPROTO);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->sizelineinfo = 0;
+ f->sizeupvalues = 0;
+ f->nups = 0;
+ f->upvalues = NULL;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->lineinfo = NULL;
+ f->sizelocvars = 0;
+ f->locvars = NULL;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ luaM_freearray(L, f->code, f->sizecode, Instruction);
+ luaM_freearray(L, f->p, f->sizep, Proto *);
+ luaM_freearray(L, f->k, f->sizek, TValue);
+ luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
+ luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
+ luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
+ luaM_free(L, f);
+}
+
+
+void luaF_freeclosure (lua_State *L, Closure *c) {
+ int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
+ sizeLclosure(c->l.nupvalues);
+ luaM_freemem(L, c, size);
+}
+
+
+/*
+** Look for n-th local variable at line `line' in function `func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return getstr(f->locvars[i].varname);
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/engines/sword25/util/lua/lfunc.h b/engines/sword25/util/lua/lfunc.h
new file mode 100644
index 0000000000..a68cf5151c
--- /dev/null
+++ b/engines/sword25/util/lua/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
+ cast(int, sizeof(TValue)*((n)-1)))
+
+#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
+ cast(int, sizeof(TValue *)*((n)-1)))
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_close (lua_State *L, StkId level);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
+LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/engines/sword25/util/lua/lgc.c b/engines/sword25/util/lua/lgc.c
new file mode 100644
index 0000000000..d9e0b78294
--- /dev/null
+++ b/engines/sword25/util/lua/lgc.c
@@ -0,0 +1,711 @@
+/*
+** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#include <string.h>
+
+#define lgc_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#define GCSTEPSIZE 1024u
+#define GCSWEEPMAX 40
+#define GCSWEEPCOST 10
+#define GCFINALIZECOST 100
+
+
+#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
+
+#define makewhite(g,x) \
+ ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))
+
+#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT)
+
+#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)
+
+
+#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT)
+#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT)
+
+
+#define KEYWEAK bitmask(KEYWEAKBIT)
+#define VALUEWEAK bitmask(VALUEWEAKBIT)
+
+
+
+#define markvalue(g,o) { checkconsistency(o); \
+ if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }
+
+#define markobject(g,t) { if (iswhite(obj2gco(t))) \
+ reallymarkobject(g, obj2gco(t)); }
+
+
+#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause)
+
+
+static void removeentry (Node *n) {
+ lua_assert(ttisnil(gval(n)));
+ if (iscollectable(gkey(n)))
+ setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */
+}
+
+
+static void reallymarkobject (global_State *g, GCObject *o) {
+ lua_assert(iswhite(o) && !isdead(g, o));
+ white2gray(o);
+ switch (o->gch.tt) {
+ case LUA_TSTRING: {
+ return;
+ }
+ case LUA_TUSERDATA: {
+ Table *mt = gco2u(o)->metatable;
+ gray2black(o); /* udata are never gray */
+ if (mt) markobject(g, mt);
+ markobject(g, gco2u(o)->env);
+ return;
+ }
+ case LUA_TUPVAL: {
+ UpVal *uv = gco2uv(o);
+ markvalue(g, uv->v);
+ if (uv->v == &uv->u.value) /* closed? */
+ gray2black(o); /* open upvalues are never black */
+ return;
+ }
+ case LUA_TFUNCTION: {
+ gco2cl(o)->c.gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TTABLE: {
+ gco2h(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TTHREAD: {
+ gco2th(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ case LUA_TPROTO: {
+ gco2p(o)->gclist = g->gray;
+ g->gray = o;
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+static void marktmu (global_State *g) {
+ GCObject *u = g->tmudata;
+ if (u) {
+ do {
+ u = u->gch.next;
+ makewhite(g, u); /* may be marked, if left from previous GC */
+ reallymarkobject(g, u);
+ } while (u != g->tmudata);
+ }
+}
+
+
+/* move `dead' udata that need finalization to list `tmudata' */
+size_t luaC_separateudata (lua_State *L, int all) {
+ global_State *g = G(L);
+ size_t deadmem = 0;
+ GCObject **p = &g->mainthread->next;
+ GCObject *curr;
+ while ((curr = *p) != NULL) {
+ if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))
+ p = &curr->gch.next; /* don't bother with them */
+ else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {
+ markfinalized(gco2u(curr)); /* don't need finalization */
+ p = &curr->gch.next;
+ }
+ else { /* must call its gc method */
+ deadmem += sizeudata(gco2u(curr));
+ markfinalized(gco2u(curr));
+ *p = curr->gch.next;
+ /* link `curr' at the end of `tmudata' list */
+ if (g->tmudata == NULL) /* list is empty? */
+ g->tmudata = curr->gch.next = curr; /* creates a circular list */
+ else {
+ curr->gch.next = g->tmudata->gch.next;
+ g->tmudata->gch.next = curr;
+ g->tmudata = curr;
+ }
+ }
+ }
+ return deadmem;
+}
+
+
+static int traversetable (global_State *g, Table *h) {
+ int i;
+ int weakkey = 0;
+ int weakvalue = 0;
+ const TValue *mode;
+ if (h->metatable)
+ markobject(g, h->metatable);
+ mode = gfasttm(g, h->metatable, TM_MODE);
+ if (mode && ttisstring(mode)) { /* is there a weak mode? */
+ weakkey = (strchr(svalue(mode), 'k') != NULL);
+ weakvalue = (strchr(svalue(mode), 'v') != NULL);
+ if (weakkey || weakvalue) { /* is really weak? */
+ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
+ h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
+ (weakvalue << VALUEWEAKBIT));
+ h->gclist = g->weak; /* must be cleared after GC, ... */
+ g->weak = obj2gco(h); /* ... so put in the appropriate list */
+ }
+ }
+ if (weakkey && weakvalue) return 1;
+ if (!weakvalue) {
+ i = h->sizearray;
+ while (i--)
+ markvalue(g, &h->array[i]);
+ }
+ i = sizenode(h);
+ while (i--) {
+ Node *n = gnode(h, i);
+ lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
+ if (ttisnil(gval(n)))
+ removeentry(n); /* remove empty entries */
+ else {
+ lua_assert(!ttisnil(gkey(n)));
+ if (!weakkey) markvalue(g, gkey(n));
+ if (!weakvalue) markvalue(g, gval(n));
+ }
+ }
+ return weakkey || weakvalue;
+}
+
+
+/*
+** All marks are conditional because a GC may happen while the
+** prototype is still being created
+*/
+static void traverseproto (global_State *g, Proto *f) {
+ int i;
+ if (f->source) stringmark(f->source);
+ for (i=0; i<f->sizek; i++) /* mark literals */
+ markvalue(g, &f->k[i]);
+ for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */
+ if (f->upvalues[i])
+ stringmark(f->upvalues[i]);
+ }
+ for (i=0; i<f->sizep; i++) { /* mark nested protos */
+ if (f->p[i])
+ markobject(g, f->p[i]);
+ }
+ for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */
+ if (f->locvars[i].varname)
+ stringmark(f->locvars[i].varname);
+ }
+}
+
+
+
+static void traverseclosure (global_State *g, Closure *cl) {
+ markobject(g, cl->c.env);
+ if (cl->c.isC) {
+ int i;
+ for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */
+ markvalue(g, &cl->c.upvalue[i]);
+ }
+ else {
+ int i;
+ lua_assert(cl->l.nupvalues == cl->l.p->nups);
+ markobject(g, cl->l.p);
+ for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */
+ markobject(g, cl->l.upvals[i]);
+ }
+}
+
+
+static void checkstacksizes (lua_State *L, StkId max) {
+ int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */
+ int s_used = cast_int(max - L->stack); /* part of stack in use */
+ if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */
+ return; /* do not touch the stacks */
+ if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
+ luaD_reallocCI(L, L->size_ci/2); /* still big enough... */
+ condhardstacktests(luaD_reallocCI(L, ci_used + 1));
+ if (4*s_used < L->stacksize &&
+ 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
+ luaD_reallocstack(L, L->stacksize/2); /* still big enough... */
+ condhardstacktests(luaD_reallocstack(L, s_used));
+}
+
+
+static void traversestack (global_State *g, lua_State *l) {
+ StkId o, lim;
+ CallInfo *ci;
+ markvalue(g, gt(l));
+ lim = l->top;
+ for (ci = l->base_ci; ci <= l->ci; ci++) {
+ lua_assert(ci->top <= l->stack_last);
+ if (lim < ci->top) lim = ci->top;
+ }
+ for (o = l->stack; o < l->top; o++)
+ markvalue(g, o);
+ for (; o <= lim; o++)
+ setnilvalue(o);
+ checkstacksizes(l, lim);
+}
+
+
+/*
+** traverse one gray object, turning it to black.
+** Returns `quantity' traversed.
+*/
+static l_mem propagatemark (global_State *g) {
+ GCObject *o = g->gray;
+ lua_assert(isgray(o));
+ gray2black(o);
+ switch (o->gch.tt) {
+ case LUA_TTABLE: {
+ Table *h = gco2h(o);
+ g->gray = h->gclist;
+ if (traversetable(g, h)) /* table is weak? */
+ black2gray(o); /* keep it gray */
+ return sizeof(Table) + sizeof(TValue) * h->sizearray +
+ sizeof(Node) * sizenode(h);
+ }
+ case LUA_TFUNCTION: {
+ Closure *cl = gco2cl(o);
+ g->gray = cl->c.gclist;
+ traverseclosure(g, cl);
+ return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
+ sizeLclosure(cl->l.nupvalues);
+ }
+ case LUA_TTHREAD: {
+ lua_State *th = gco2th(o);
+ g->gray = th->gclist;
+ th->gclist = g->grayagain;
+ g->grayagain = o;
+ black2gray(o);
+ traversestack(g, th);
+ return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
+ sizeof(CallInfo) * th->size_ci;
+ }
+ case LUA_TPROTO: {
+ Proto *p = gco2p(o);
+ g->gray = p->gclist;
+ traverseproto(g, p);
+ return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
+ sizeof(Proto *) * p->sizep +
+ sizeof(TValue) * p->sizek +
+ sizeof(int) * p->sizelineinfo +
+ sizeof(LocVar) * p->sizelocvars +
+ sizeof(TString *) * p->sizeupvalues;
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static size_t propagateall (global_State *g) {
+ size_t m = 0;
+ while (g->gray) m += propagatemark(g);
+ return m;
+}
+
+
+/*
+** The next function tells whether a key or value can be cleared from
+** a weak table. Non-collectable objects are never removed from weak
+** tables. Strings behave as `values', so are never removed too. for
+** other objects: if really collected, cannot keep them; for userdata
+** being finalized, keep them in keys, but not in values
+*/
+static int iscleared (const TValue *o, int iskey) {
+ if (!iscollectable(o)) return 0;
+ if (ttisstring(o)) {
+ stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
+ return 0;
+ }
+ return iswhite(gcvalue(o)) ||
+ (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
+}
+
+
+/*
+** clear collected entries from weaktables
+*/
+static void cleartable (GCObject *l) {
+ while (l) {
+ Table *h = gco2h(l);
+ int i = h->sizearray;
+ lua_assert(testbit(h->marked, VALUEWEAKBIT) ||
+ testbit(h->marked, KEYWEAKBIT));
+ if (testbit(h->marked, VALUEWEAKBIT)) {
+ while (i--) {
+ TValue *o = &h->array[i];
+ if (iscleared(o, 0)) /* value was collected? */
+ setnilvalue(o); /* remove value */
+ }
+ }
+ i = sizenode(h);
+ while (i--) {
+ Node *n = gnode(h, i);
+ if (!ttisnil(gval(n)) && /* non-empty entry? */
+ (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {
+ setnilvalue(gval(n)); /* remove value ... */
+ removeentry(n); /* remove entry from table */
+ }
+ }
+ l = h->gclist;
+ }
+}
+
+
+static void freeobj (lua_State *L, GCObject *o) {
+ switch (o->gch.tt) {
+ case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
+ case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
+ case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
+ case LUA_TTABLE: luaH_free(L, gco2h(o)); break;
+ case LUA_TTHREAD: {
+ lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);
+ luaE_freethread(L, gco2th(o));
+ break;
+ }
+ case LUA_TSTRING: {
+ G(L)->strt.nuse--;
+ luaM_freemem(L, o, sizestring(gco2ts(o)));
+ break;
+ }
+ case LUA_TUSERDATA: {
+ luaM_freemem(L, o, sizeudata(gco2u(o)));
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+
+#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM)
+
+
+static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
+ GCObject *curr;
+ global_State *g = G(L);
+ int deadmask = otherwhite(g);
+ while ((curr = *p) != NULL && count-- > 0) {
+ if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */
+ sweepwholelist(L, &gco2th(curr)->openupval);
+ if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */
+ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
+ makewhite(g, curr); /* make it white (for next cycle) */
+ p = &curr->gch.next;
+ }
+ else { /* must erase `curr' */
+ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
+ *p = curr->gch.next;
+ if (curr == g->rootgc) /* is the first element of the list? */
+ g->rootgc = curr->gch.next; /* adjust first */
+ freeobj(L, curr);
+ }
+ }
+ return p;
+}
+
+
+static void checkSizes (lua_State *L) {
+ global_State *g = G(L);
+ /* check size of string hash */
+ if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
+ g->strt.size > MINSTRTABSIZE*2)
+ luaS_resize(L, g->strt.size/2); /* table is too big */
+ /* check size of buffer */
+ if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */
+ size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
+ luaZ_resizebuffer(L, &g->buff, newsize);
+ }
+}
+
+
+static void GCTM (lua_State *L) {
+ global_State *g = G(L);
+ GCObject *o = g->tmudata->gch.next; /* get first element */
+ Udata *udata = rawgco2u(o);
+ const TValue *tm;
+ /* remove udata from `tmudata' */
+ if (o == g->tmudata) /* last element? */
+ g->tmudata = NULL;
+ else
+ g->tmudata->gch.next = udata->uv.next;
+ udata->uv.next = g->mainthread->next; /* return it to `root' list */
+ g->mainthread->next = o;
+ makewhite(g, o);
+ tm = fasttm(L, udata->uv.metatable, TM_GC);
+ if (tm != NULL) {
+ lu_byte oldah = L->allowhook;
+ lu_mem oldt = g->GCthreshold;
+ L->allowhook = 0; /* stop debug hooks during GC tag method */
+ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */
+ setobj2s(L, L->top, tm);
+ setuvalue(L, L->top+1, udata);
+ L->top += 2;
+ luaD_call(L, L->top - 2, 0);
+ L->allowhook = oldah; /* restore hooks */
+ g->GCthreshold = oldt; /* restore threshold */
+ }
+}
+
+
+/*
+** Call all GC tag methods
+*/
+void luaC_callGCTM (lua_State *L) {
+ while (G(L)->tmudata)
+ GCTM(L);
+}
+
+
+void luaC_freeall (lua_State *L) {
+ global_State *g = G(L);
+ int i;
+ g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */
+ sweepwholelist(L, &g->rootgc);
+ for (i = 0; i < g->strt.size; i++) /* free all string lists */
+ sweepwholelist(L, &g->strt.hash[i]);
+}
+
+
+static void markmt (global_State *g) {
+ int i;
+ for (i=0; i<NUM_TAGS; i++)
+ if (g->mt[i]) markobject(g, g->mt[i]);
+}
+
+
+/* mark root set */
+static void markroot (lua_State *L) {
+ global_State *g = G(L);
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ markobject(g, g->mainthread);
+ /* make global table be traversed before main stack */
+ markvalue(g, gt(g->mainthread));
+ markvalue(g, registry(L));
+ markmt(g);
+ g->gcstate = GCSpropagate;
+}
+
+
+static void remarkupvals (global_State *g) {
+ UpVal *uv;
+ for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ if (isgray(obj2gco(uv)))
+ markvalue(g, uv->v);
+ }
+}
+
+
+static void atomic (lua_State *L) {
+ global_State *g = G(L);
+ size_t udsize; /* total size of userdata to be finalized */
+ /* remark occasional upvalues of (maybe) dead threads */
+ remarkupvals(g);
+ /* traverse objects cautch by write barrier and by 'remarkupvals' */
+ propagateall(g);
+ /* remark weak tables */
+ g->gray = g->weak;
+ g->weak = NULL;
+ lua_assert(!iswhite(obj2gco(g->mainthread)));
+ markobject(g, L); /* mark running thread */
+ markmt(g); /* mark basic metatables (again) */
+ propagateall(g);
+ /* remark gray again */
+ g->gray = g->grayagain;
+ g->grayagain = NULL;
+ propagateall(g);
+ udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */
+ marktmu(g); /* mark `preserved' userdata */
+ udsize += propagateall(g); /* remark, to propagate `preserveness' */
+ cleartable(g->weak); /* remove collected objects from weak tables */
+ /* flip current white */
+ g->currentwhite = cast_byte(otherwhite(g));
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ g->gcstate = GCSsweepstring;
+ g->estimate = g->totalbytes - udsize; /* first estimate */
+}
+
+
+static l_mem singlestep (lua_State *L) {
+ global_State *g = G(L);
+ /*lua_checkmemory(L);*/
+ switch (g->gcstate) {
+ case GCSpause: {
+ markroot(L); /* start a new collection */
+ return 0;
+ }
+ case GCSpropagate: {
+ if (g->gray)
+ return propagatemark(g);
+ else { /* no more `gray' objects */
+ atomic(L); /* finish mark phase */
+ return 0;
+ }
+ }
+ case GCSsweepstring: {
+ lu_mem old = g->totalbytes;
+ sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
+ if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */
+ g->gcstate = GCSsweep; /* end sweep-string phase */
+ lua_assert(old >= g->totalbytes);
+ g->estimate -= old - g->totalbytes;
+ return GCSWEEPCOST;
+ }
+ case GCSsweep: {
+ lu_mem old = g->totalbytes;
+ g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
+ if (*g->sweepgc == NULL) { /* nothing more to sweep? */
+ checkSizes(L);
+ g->gcstate = GCSfinalize; /* end sweep phase */
+ }
+ lua_assert(old >= g->totalbytes);
+ g->estimate -= old - g->totalbytes;
+ return GCSWEEPMAX*GCSWEEPCOST;
+ }
+ case GCSfinalize: {
+ if (g->tmudata) {
+ GCTM(L);
+ if (g->estimate > GCFINALIZECOST)
+ g->estimate -= GCFINALIZECOST;
+ return GCFINALIZECOST;
+ }
+ else {
+ g->gcstate = GCSpause; /* end collection */
+ g->gcdept = 0;
+ return 0;
+ }
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+void luaC_step (lua_State *L) {
+ global_State *g = G(L);
+ l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
+ if (lim == 0)
+ lim = (MAX_LUMEM-1)/2; /* no limit */
+ g->gcdept += g->totalbytes - g->GCthreshold;
+ do {
+ lim -= singlestep(L);
+ if (g->gcstate == GCSpause)
+ break;
+ } while (lim > 0);
+ if (g->gcstate != GCSpause) {
+ if (g->gcdept < GCSTEPSIZE)
+ g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/
+ else {
+ g->gcdept -= GCSTEPSIZE;
+ g->GCthreshold = g->totalbytes;
+ }
+ }
+ else {
+ lua_assert(g->totalbytes >= g->estimate);
+ setthreshold(g);
+ }
+}
+
+
+void luaC_fullgc (lua_State *L) {
+ global_State *g = G(L);
+ if (g->gcstate <= GCSpropagate) {
+ /* reset sweep marks to sweep all elements (returning them to white) */
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ /* reset other collector lists */
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ g->gcstate = GCSsweepstring;
+ }
+ lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);
+ /* finish any pending sweep phase */
+ while (g->gcstate != GCSfinalize) {
+ lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
+ singlestep(L);
+ }
+ markroot(L);
+ while (g->gcstate != GCSpause) {
+ singlestep(L);
+ }
+ setthreshold(g);
+}
+
+
+void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ lua_assert(ttype(&o->gch) != LUA_TTABLE);
+ /* must keep invariant? */
+ if (g->gcstate == GCSpropagate)
+ reallymarkobject(g, v); /* restore invariant */
+ else /* don't mind */
+ makewhite(g, o); /* mark as white just to avoid other barriers */
+}
+
+
+void luaC_barrierback (lua_State *L, Table *t) {
+ global_State *g = G(L);
+ GCObject *o = obj2gco(t);
+ lua_assert(isblack(o) && !isdead(g, o));
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ black2gray(o); /* make table gray (again) */
+ t->gclist = g->grayagain;
+ g->grayagain = o;
+}
+
+
+void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
+ global_State *g = G(L);
+ o->gch.next = g->rootgc;
+ g->rootgc = o;
+ o->gch.marked = luaC_white(g);
+ o->gch.tt = tt;
+}
+
+
+void luaC_linkupval (lua_State *L, UpVal *uv) {
+ global_State *g = G(L);
+ GCObject *o = obj2gco(uv);
+ o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */
+ g->rootgc = o;
+ if (isgray(o)) {
+ if (g->gcstate == GCSpropagate) {
+ gray2black(o); /* closed upvalues need barrier */
+ luaC_barrier(L, uv, uv->v);
+ }
+ else { /* sweep phase: sweep it (turning it into white) */
+ makewhite(g, o);
+ lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
+ }
+ }
+}
+
diff --git a/engines/sword25/util/lua/lgc.h b/engines/sword25/util/lua/lgc.h
new file mode 100644
index 0000000000..5a8dc605b3
--- /dev/null
+++ b/engines/sword25/util/lua/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpause 0
+#define GCSpropagate 1
+#define GCSsweepstring 2
+#define GCSsweep 3
+#define GCSfinalize 4
+
+
+/*
+** some userful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
+#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
+#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
+
+
+
+/*
+** Layout for bit use in `marked' field:
+** bit 0 - object is white (type 0)
+** bit 1 - object is white (type 1)
+** bit 2 - object is black
+** bit 3 - for userdata: has been finalized
+** bit 3 - for tables: has weak keys
+** bit 4 - for tables: has weak values
+** bit 5 - object is fixed (should not be collected)
+** bit 6 - object is "super" fixed (only the main thread)
+*/
+
+
+#define WHITE0BIT 0
+#define WHITE1BIT 1
+#define BLACKBIT 2
+#define FINALIZEDBIT 3
+#define KEYWEAKBIT 3
+#define VALUEWEAKBIT 4
+#define FIXEDBIT 5
+#define SFIXEDBIT 6
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
+#define isgray(x) (!isblack(x) && !iswhite(x))
+
+#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
+#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
+
+#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
+#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
+
+
+#define luaC_checkGC(L) { \
+ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
+ if (G(L)->totalbytes >= G(L)->GCthreshold) \
+ luaC_step(L); }
+
+
+#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
+
+#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
+ luaC_barrierback(L,t); }
+
+#define luaC_objbarrier(L,p,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
+
+#define luaC_objbarriert(L,t,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
+
+LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
+LUAI_FUNC void luaC_callGCTM (lua_State *L);
+LUAI_FUNC void luaC_freeall (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_fullgc (lua_State *L);
+LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
+LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
+LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
+
+
+#endif
diff --git a/engines/sword25/util/lua/linit.c b/engines/sword25/util/lua/linit.c
new file mode 100644
index 0000000000..c1f90dfab7
--- /dev/null
+++ b/engines/sword25/util/lua/linit.c
@@ -0,0 +1,38 @@
+/*
+** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
+** Initialization of libraries for lua.c
+** See Copyright Notice in lua.h
+*/
+
+
+#define linit_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+
+
+static const luaL_Reg lualibs[] = {
+ {"", luaopen_base},
+ {LUA_LOADLIBNAME, luaopen_package},
+ {LUA_TABLIBNAME, luaopen_table},
+ {LUA_IOLIBNAME, luaopen_io},
+ {LUA_OSLIBNAME, luaopen_os},
+ {LUA_STRLIBNAME, luaopen_string},
+ {LUA_MATHLIBNAME, luaopen_math},
+ {LUA_DBLIBNAME, luaopen_debug},
+ {NULL, NULL}
+};
+
+
+LUALIB_API void luaL_openlibs (lua_State *L) {
+ const luaL_Reg *lib = lualibs;
+ for (; lib->func; lib++) {
+ lua_pushcfunction(L, lib->func);
+ lua_pushstring(L, lib->name);
+ lua_call(L, 1, 0);
+ }
+}
+
diff --git a/engines/sword25/util/lua/liolib.c b/engines/sword25/util/lua/liolib.c
new file mode 100644
index 0000000000..e79ed1cb2e
--- /dev/null
+++ b/engines/sword25/util/lua/liolib.c
@@ -0,0 +1,553 @@
+/*
+** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define liolib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+#define IO_INPUT 1
+#define IO_OUTPUT 2
+
+
+static const char *const fnames[] = {"input", "output"};
+
+
+static int pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ if (filename)
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ else
+ lua_pushfstring(L, "%s", strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+static void fileerror (lua_State *L, int arg, const char *filename) {
+ lua_pushfstring(L, "%s: %s", filename, strerror(errno));
+ luaL_argerror(L, arg, lua_tostring(L, -1));
+}
+
+
+#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+
+
+static int io_type (lua_State *L) {
+ void *ud;
+ luaL_checkany(L, 1);
+ ud = lua_touserdata(L, 1);
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
+ if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
+ lua_pushnil(L); /* not a file */
+ else if (*((FILE **)ud) == NULL)
+ lua_pushliteral(L, "closed file");
+ else
+ lua_pushliteral(L, "file");
+ return 1;
+}
+
+
+static FILE *tofile (lua_State *L) {
+ FILE **f = tofilep(L);
+ if (*f == NULL)
+ luaL_error(L, "attempt to use a closed file");
+ return *f;
+}
+
+
+
+/*
+** When creating file handles, always creates a `closed' file handle
+** before opening the actual file; so, if there is a memory error, the
+** file is not left opened.
+*/
+static FILE **newfile (lua_State *L) {
+ FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
+ *pf = NULL; /* file handle is currently `closed' */
+ luaL_getmetatable(L, LUA_FILEHANDLE);
+ lua_setmetatable(L, -2);
+ return pf;
+}
+
+
+/*
+** function to (not) close the standard files stdin, stdout, and stderr
+*/
+static int io_noclose (lua_State *L) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "cannot close standard file");
+ return 2;
+}
+
+
+/*
+** function to close 'popen' files
+*/
+static int io_pclose (lua_State *L) {
+ FILE **p = tofilep(L);
+ int ok = lua_pclose(L, *p);
+ *p = NULL;
+ return pushresult(L, ok, NULL);
+}
+
+
+/*
+** function to close regular files
+*/
+static int io_fclose (lua_State *L) {
+ FILE **p = tofilep(L);
+ int ok = (fclose(*p) == 0);
+ *p = NULL;
+ return pushresult(L, ok, NULL);
+}
+
+
+static int aux_close (lua_State *L) {
+ lua_getfenv(L, 1);
+ lua_getfield(L, -1, "__close");
+ return (lua_tocfunction(L, -1))(L);
+}
+
+
+static int io_close (lua_State *L) {
+ if (lua_isnone(L, 1))
+ lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
+ tofile(L); /* make sure argument is a file */
+ return aux_close(L);
+}
+
+
+static int io_gc (lua_State *L) {
+ FILE *f = *tofilep(L);
+ /* ignore closed files */
+ if (f != NULL)
+ aux_close(L);
+ return 0;
+}
+
+
+static int io_tostring (lua_State *L) {
+ FILE *f = *tofilep(L);
+ if (f == NULL)
+ lua_pushliteral(L, "file (closed)");
+ else
+ lua_pushfstring(L, "file (%p)", f);
+ return 1;
+}
+
+
+static int io_open (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, mode);
+ return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
+}
+
+
+/*
+** this function has a separated environment, which defines the
+** correct __close for 'popen' files
+*/
+static int io_popen (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ FILE **pf = newfile(L);
+ *pf = lua_popen(L, filename, mode);
+ return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
+}
+
+
+static int io_tmpfile (lua_State *L) {
+ FILE **pf = newfile(L);
+ *pf = tmpfile();
+ return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
+}
+
+
+static FILE *getiofile (lua_State *L, int findex) {
+ FILE *f;
+ lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
+ f = *(FILE **)lua_touserdata(L, -1);
+ if (f == NULL)
+ luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
+ return f;
+}
+
+
+static int g_iofile (lua_State *L, int f, const char *mode) {
+ if (!lua_isnoneornil(L, 1)) {
+ const char *filename = lua_tostring(L, 1);
+ if (filename) {
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, mode);
+ if (*pf == NULL)
+ fileerror(L, 1, filename);
+ }
+ else {
+ tofile(L); /* check that it's a valid file handle */
+ lua_pushvalue(L, 1);
+ }
+ lua_rawseti(L, LUA_ENVIRONINDEX, f);
+ }
+ /* return current value */
+ lua_rawgeti(L, LUA_ENVIRONINDEX, f);
+ return 1;
+}
+
+
+static int io_input (lua_State *L) {
+ return g_iofile(L, IO_INPUT, "r");
+}
+
+
+static int io_output (lua_State *L) {
+ return g_iofile(L, IO_OUTPUT, "w");
+}
+
+
+static int io_readline (lua_State *L);
+
+
+static void aux_lines (lua_State *L, int idx, int toclose) {
+ lua_pushvalue(L, idx);
+ lua_pushboolean(L, toclose); /* close/not close file when finished */
+ lua_pushcclosure(L, io_readline, 2);
+}
+
+
+static int f_lines (lua_State *L) {
+ tofile(L); /* check that it's a valid file handle */
+ aux_lines(L, 1, 0);
+ return 1;
+}
+
+
+static int io_lines (lua_State *L) {
+ if (lua_isnoneornil(L, 1)) { /* no arguments? */
+ /* will iterate over default input */
+ lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
+ return f_lines(L);
+ }
+ else {
+ const char *filename = luaL_checkstring(L, 1);
+ FILE **pf = newfile(L);
+ *pf = fopen(filename, "r");
+ if (*pf == NULL)
+ fileerror(L, 1, filename);
+ aux_lines(L, lua_gettop(L), 1);
+ return 1;
+ }
+}
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+static int read_number (lua_State *L, FILE *f) {
+ lua_Number d;
+ if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ else return 0; /* read fails */
+}
+
+
+static int test_eof (lua_State *L, FILE *f) {
+ int c = getc(f);
+ ungetc(c, f);
+ lua_pushlstring(L, NULL, 0);
+ return (c != EOF);
+}
+
+
+static int read_line (lua_State *L, FILE *f) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (;;) {
+ size_t l;
+ char *p = luaL_prepbuffer(&b);
+ if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
+ luaL_pushresult(&b); /* close buffer */
+ return (lua_objlen(L, -1) > 0); /* check whether read something */
+ }
+ l = strlen(p);
+ if (l == 0 || p[l-1] != '\n')
+ luaL_addsize(&b, l);
+ else {
+ luaL_addsize(&b, l - 1); /* do not include `eol' */
+ luaL_pushresult(&b); /* close buffer */
+ return 1; /* read at least an `eol' */
+ }
+ }
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ size_t rlen; /* how much to read */
+ size_t nr; /* number of chars actually read */
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
+ do {
+ char *p = luaL_prepbuffer(&b);
+ if (rlen > n) rlen = n; /* cannot read more than asked */
+ nr = fread(p, sizeof(char), rlen, f);
+ luaL_addsize(&b, nr);
+ n -= nr; /* still have to read `n' chars */
+ } while (n > 0 && nr == rlen); /* until end of count or eof */
+ luaL_pushresult(&b); /* close buffer */
+ return (n == 0 || lua_objlen(L, -1) > 0);
+}
+
+
+static int g_read (lua_State *L, FILE *f, int first) {
+ int nargs = lua_gettop(L) - 1;
+ int success;
+ int n;
+ clearerr(f);
+ if (nargs == 0) { /* no arguments? */
+ success = read_line(L, f);
+ n = first+1; /* to return 1 result */
+ }
+ else { /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = first; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)lua_tointeger(L, n);
+ success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
+ }
+ else {
+ const char *p = lua_tostring(L, n);
+ luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
+ switch (p[1]) {
+ case 'n': /* number */
+ success = read_number(L, f);
+ break;
+ case 'l': /* line */
+ success = read_line(L, f);
+ break;
+ case 'a': /* file */
+ read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
+ success = 1; /* always success */
+ break;
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+ }
+ if (ferror(f))
+ return pushresult(L, 0, NULL);
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ lua_pushnil(L); /* push nil instead */
+ }
+ return n - first;
+}
+
+
+static int io_read (lua_State *L) {
+ return g_read(L, getiofile(L, IO_INPUT), 1);
+}
+
+
+static int f_read (lua_State *L) {
+ return g_read(L, tofile(L), 2);
+}
+
+
+static int io_readline (lua_State *L) {
+ FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
+ int sucess;
+ if (f == NULL) /* file is already closed? */
+ luaL_error(L, "file is already closed");
+ sucess = read_line(L, f);
+ if (ferror(f))
+ return luaL_error(L, "%s", strerror(errno));
+ if (sucess) return 1;
+ else { /* EOF */
+ if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
+ lua_settop(L, 0);
+ lua_pushvalue(L, lua_upvalueindex(1));
+ aux_close(L); /* close it */
+ }
+ return 0;
+ }
+}
+
+/* }====================================================== */
+
+
+static int g_write (lua_State *L, FILE *f, int arg) {
+ int nargs = lua_gettop(L) - 1;
+ int status = 1;
+ for (; nargs--; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) {
+ /* optimization: could be done exactly as for strings */
+ status = status &&
+ fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
+ }
+ else {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ return pushresult(L, status, NULL);
+}
+
+
+static int io_write (lua_State *L) {
+ return g_write(L, getiofile(L, IO_OUTPUT), 1);
+}
+
+
+static int f_write (lua_State *L) {
+ return g_write(L, tofile(L), 2);
+}
+
+
+static int f_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ long offset = luaL_optlong(L, 3, 0);
+ op = fseek(f, offset, mode[op]);
+ if (op)
+ return pushresult(L, 0, NULL); /* error */
+ else {
+ lua_pushinteger(L, ftell(f));
+ return 1;
+ }
+}
+
+
+static int f_setvbuf (lua_State *L) {
+ static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
+ static const char *const modenames[] = {"no", "full", "line", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, NULL, modenames);
+ lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
+ int res = setvbuf(f, NULL, mode[op], sz);
+ return pushresult(L, res == 0, NULL);
+}
+
+
+
+static int io_flush (lua_State *L) {
+ return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
+}
+
+
+static int f_flush (lua_State *L) {
+ return pushresult(L, fflush(tofile(L)) == 0, NULL);
+}
+
+
+static const luaL_Reg iolib[] = {
+ {"close", io_close},
+ {"flush", io_flush},
+ {"input", io_input},
+ {"lines", io_lines},
+ {"open", io_open},
+ {"output", io_output},
+ {"popen", io_popen},
+ {"read", io_read},
+ {"tmpfile", io_tmpfile},
+ {"type", io_type},
+ {"write", io_write},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg flib[] = {
+ {"close", io_close},
+ {"flush", f_flush},
+ {"lines", f_lines},
+ {"read", f_read},
+ {"seek", f_seek},
+ {"setvbuf", f_setvbuf},
+ {"write", f_write},
+ {"__gc", io_gc},
+ {"__tostring", io_tostring},
+ {NULL, NULL}
+};
+
+
+static void createmeta (lua_State *L) {
+ luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
+ lua_pushvalue(L, -1); /* push metatable */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
+ luaL_register(L, NULL, flib); /* file methods */
+}
+
+
+static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
+ *newfile(L) = f;
+ if (k > 0) {
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, LUA_ENVIRONINDEX, k);
+ }
+ lua_pushvalue(L, -2); /* copy environment */
+ lua_setfenv(L, -2); /* set it */
+ lua_setfield(L, -3, fname);
+}
+
+
+static void newfenv (lua_State *L, lua_CFunction cls) {
+ lua_createtable(L, 0, 1);
+ lua_pushcfunction(L, cls);
+ lua_setfield(L, -2, "__close");
+}
+
+
+LUALIB_API int luaopen_io (lua_State *L) {
+ createmeta(L);
+ /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
+ newfenv(L, io_fclose);
+ lua_replace(L, LUA_ENVIRONINDEX);
+ /* open library */
+ luaL_register(L, LUA_IOLIBNAME, iolib);
+ /* create (and set) default files */
+ newfenv(L, io_noclose); /* close function for default files */
+ createstdfile(L, stdin, IO_INPUT, "stdin");
+ createstdfile(L, stdout, IO_OUTPUT, "stdout");
+ createstdfile(L, stderr, 0, "stderr");
+ lua_pop(L, 1); /* pop environment for default files */
+ lua_getfield(L, -1, "popen");
+ newfenv(L, io_pclose); /* create environment for 'popen' */
+ lua_setfenv(L, -2); /* set fenv for 'popen' */
+ lua_pop(L, 1); /* pop 'popen' */
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/llex.c b/engines/sword25/util/lua/llex.c
new file mode 100644
index 0000000000..6dc319358c
--- /dev/null
+++ b/engines/sword25/util/lua/llex.c
@@ -0,0 +1,461 @@
+/*
+** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <locale.h>
+#include <string.h>
+
+#define llex_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "llex.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lzio.h"
+
+
+
+#define next(ls) (ls->current = zgetc(ls->z))
+
+
+
+
+#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
+
+
+/* ORDER RESERVED */
+const char *const luaX_tokens [] = {
+ "and", "break", "do", "else", "elseif",
+ "end", "false", "for", "function", "if",
+ "in", "local", "nil", "not", "or", "repeat",
+ "return", "then", "true", "until", "while",
+ "..", "...", "==", ">=", "<=", "~=",
+ "<number>", "<name>", "<string>", "<eof>",
+ NULL
+};
+
+
+#define save_and_next(ls) (save(ls, ls->current), next(ls))
+
+
+static void save (LexState *ls, int c) {
+ Mbuffer *b = ls->buff;
+ if (b->n + 1 > b->buffsize) {
+ size_t newsize;
+ if (b->buffsize >= MAX_SIZET/2)
+ luaX_lexerror(ls, "lexical element too long", 0);
+ newsize = b->buffsize * 2;
+ luaZ_resizebuffer(ls->L, b, newsize);
+ }
+ b->buffer[b->n++] = cast(char, c);
+}
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, luaX_tokens[i]);
+ luaS_fix(ts); /* reserved words are never collected */
+ lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
+ ts->tsv.reserved = cast_byte(i+1); /* reserved word */
+ }
+}
+
+
+#define MAXSRC 80
+
+
+const char *luaX_token2str (LexState *ls, int token) {
+ if (token < FIRST_RESERVED) {
+ lua_assert(token == cast(unsigned char, token));
+ return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
+ luaO_pushfstring(ls->L, "%c", token);
+ }
+ else
+ return luaX_tokens[token-FIRST_RESERVED];
+}
+
+
+static const char *txtToken (LexState *ls, int token) {
+ switch (token) {
+ case TK_NAME:
+ case TK_STRING:
+ case TK_NUMBER:
+ save(ls, '\0');
+ return luaZ_buffer(ls->buff);
+ default:
+ return luaX_token2str(ls, token);
+ }
+}
+
+
+void luaX_lexerror (LexState *ls, const char *msg, int token) {
+ char buff[MAXSRC];
+ luaO_chunkid(buff, getstr(ls->source), MAXSRC);
+ msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
+ if (token)
+ luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
+ luaD_throw(ls->L, LUA_ERRSYNTAX);
+}
+
+
+void luaX_syntaxerror (LexState *ls, const char *msg) {
+ luaX_lexerror(ls, msg, ls->t.token);
+}
+
+
+TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
+ lua_State *L = ls->L;
+ TString *ts = luaS_newlstr(L, str, l);
+ TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
+ if (ttisnil(o))
+ setbvalue(o, 1); /* make sure `str' will not be collected */
+ return ts;
+}
+
+
+static void inclinenumber (LexState *ls) {
+ int old = ls->current;
+ lua_assert(currIsNewline(ls));
+ next(ls); /* skip `\n' or `\r' */
+ if (currIsNewline(ls) && ls->current != old)
+ next(ls); /* skip `\n\r' or `\r\n' */
+ if (++ls->linenumber >= MAX_INT)
+ luaX_syntaxerror(ls, "chunk has too many lines");
+}
+
+
+void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
+ ls->decpoint = '.';
+ ls->L = L;
+ ls->lookahead.token = TK_EOS; /* no look-ahead token */
+ ls->z = z;
+ ls->fs = NULL;
+ ls->linenumber = 1;
+ ls->lastline = 1;
+ ls->source = source;
+ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
+ next(ls); /* read first char */
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+
+static int check_next (LexState *ls, const char *set) {
+ if (!strchr(set, ls->current))
+ return 0;
+ save_and_next(ls);
+ return 1;
+}
+
+
+static void buffreplace (LexState *ls, char from, char to) {
+ size_t n = luaZ_bufflen(ls->buff);
+ char *p = luaZ_buffer(ls->buff);
+ while (n--)
+ if (p[n] == from) p[n] = to;
+}
+
+
+static void trydecpoint (LexState *ls, SemInfo *seminfo) {
+ /* format error: try to update decimal point separator */
+ struct lconv *cv = localeconv();
+ char old = ls->decpoint;
+ ls->decpoint = (cv ? cv->decimal_point[0] : '.');
+ buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
+ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
+ /* format error with correct decimal point: no more options */
+ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
+ luaX_lexerror(ls, "malformed number", TK_NUMBER);
+ }
+}
+
+
+/* LUA_NUMBER */
+static void read_numeral (LexState *ls, SemInfo *seminfo) {
+ lua_assert(isdigit(ls->current));
+ do {
+ save_and_next(ls);
+ } while (isdigit(ls->current) || ls->current == '.');
+ if (check_next(ls, "Ee")) /* `E'? */
+ check_next(ls, "+-"); /* optional exponent sign */
+ while (isalnum(ls->current) || ls->current == '_')
+ save_and_next(ls);
+ save(ls, '\0');
+ buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
+ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
+ trydecpoint(ls, seminfo); /* try to update decimal point separator */
+}
+
+
+static int skip_sep (LexState *ls) {
+ int count = 0;
+ int s = ls->current;
+ lua_assert(s == '[' || s == ']');
+ save_and_next(ls);
+ while (ls->current == '=') {
+ save_and_next(ls);
+ count++;
+ }
+ return (ls->current == s) ? count : (-count) - 1;
+}
+
+
+static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
+ int cont = 0;
+ (void)(cont); /* avoid warnings when `cont' is not used */
+ save_and_next(ls); /* skip 2nd `[' */
+ if (currIsNewline(ls)) /* string starts with a newline? */
+ inclinenumber(ls); /* skip it */
+ for (;;) {
+ switch (ls->current) {
+ case EOZ:
+ luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
+ "unfinished long comment", TK_EOS);
+ break; /* to avoid warnings */
+#if defined(LUA_COMPAT_LSTR)
+ case '[': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd `[' */
+ cont++;
+#if LUA_COMPAT_LSTR == 1
+ if (sep == 0)
+ luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
+#endif
+ }
+ break;
+ }
+#endif
+ case ']': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd `]' */
+#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
+ cont--;
+ if (sep == 0 && cont >= 0) break;
+#endif
+ goto endloop;
+ }
+ break;
+ }
+ case '\n':
+ case '\r': {
+ save(ls, '\n');
+ inclinenumber(ls);
+ if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
+ break;
+ }
+ default: {
+ if (seminfo) save_and_next(ls);
+ else next(ls);
+ }
+ }
+ } endloop:
+ if (seminfo)
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
+ luaZ_bufflen(ls->buff) - 2*(2 + sep));
+}
+
+
+static void read_string (LexState *ls, int del, SemInfo *seminfo) {
+ save_and_next(ls);
+ while (ls->current != del) {
+ switch (ls->current) {
+ case EOZ:
+ luaX_lexerror(ls, "unfinished string", TK_EOS);
+ continue; /* to avoid warnings */
+ case '\n':
+ case '\r':
+ luaX_lexerror(ls, "unfinished string", TK_STRING);
+ continue; /* to avoid warnings */
+ case '\\': {
+ int c;
+ next(ls); /* do not save the `\' */
+ switch (ls->current) {
+ case 'a': c = '\a'; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ case '\n': /* go through */
+ case '\r': save(ls, '\n'); inclinenumber(ls); continue;
+ case EOZ: continue; /* will raise an error next loop */
+ default: {
+ if (!isdigit(ls->current))
+ save_and_next(ls); /* handles \\, \", \', and \? */
+ else { /* \xxx */
+ int i = 0;
+ c = 0;
+ do {
+ c = 10*c + (ls->current-'0');
+ next(ls);
+ } while (++i<3 && isdigit(ls->current));
+ if (c > UCHAR_MAX)
+ luaX_lexerror(ls, "escape sequence too large", TK_STRING);
+ save(ls, c);
+ }
+ continue;
+ }
+ }
+ save(ls, c);
+ next(ls);
+ continue;
+ }
+ default:
+ save_and_next(ls);
+ }
+ }
+ save_and_next(ls); /* skip delimiter */
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
+ luaZ_bufflen(ls->buff) - 2);
+}
+
+
+static int llex (LexState *ls, SemInfo *seminfo) {
+ luaZ_resetbuffer(ls->buff);
+ for (;;) {
+ switch (ls->current) {
+ case '\n':
+ case '\r': {
+ inclinenumber(ls);
+ continue;
+ }
+ case '-': {
+ next(ls);
+ if (ls->current != '-') return '-';
+ /* else is a comment */
+ next(ls);
+ if (ls->current == '[') {
+ int sep = skip_sep(ls);
+ luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
+ if (sep >= 0) {
+ read_long_string(ls, NULL, sep); /* long comment */
+ luaZ_resetbuffer(ls->buff);
+ continue;
+ }
+ }
+ /* else short comment */
+ while (!currIsNewline(ls) && ls->current != EOZ)
+ next(ls);
+ continue;
+ }
+ case '[': {
+ int sep = skip_sep(ls);
+ if (sep >= 0) {
+ read_long_string(ls, seminfo, sep);
+ return TK_STRING;
+ }
+ else if (sep == -1) return '[';
+ else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
+ }
+ case '=': {
+ next(ls);
+ if (ls->current != '=') return '=';
+ else { next(ls); return TK_EQ; }
+ }
+ case '<': {
+ next(ls);
+ if (ls->current != '=') return '<';
+ else { next(ls); return TK_LE; }
+ }
+ case '>': {
+ next(ls);
+ if (ls->current != '=') return '>';
+ else { next(ls); return TK_GE; }
+ }
+ case '~': {
+ next(ls);
+ if (ls->current != '=') return '~';
+ else { next(ls); return TK_NE; }
+ }
+ case '"':
+ case '\'': {
+ read_string(ls, ls->current, seminfo);
+ return TK_STRING;
+ }
+ case '.': {
+ save_and_next(ls);
+ if (check_next(ls, ".")) {
+ if (check_next(ls, "."))
+ return TK_DOTS; /* ... */
+ else return TK_CONCAT; /* .. */
+ }
+ else if (!isdigit(ls->current)) return '.';
+ else {
+ read_numeral(ls, seminfo);
+ return TK_NUMBER;
+ }
+ }
+ case EOZ: {
+ return TK_EOS;
+ }
+ default: {
+ if (isspace(ls->current)) {
+ lua_assert(!currIsNewline(ls));
+ next(ls);
+ continue;
+ }
+ else if (isdigit(ls->current)) {
+ read_numeral(ls, seminfo);
+ return TK_NUMBER;
+ }
+ else if (isalpha(ls->current) || ls->current == '_') {
+ /* identifier or reserved word */
+ TString *ts;
+ do {
+ save_and_next(ls);
+ } while (isalnum(ls->current) || ls->current == '_');
+ ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
+ luaZ_bufflen(ls->buff));
+ if (ts->tsv.reserved > 0) /* reserved word? */
+ return ts->tsv.reserved - 1 + FIRST_RESERVED;
+ else {
+ seminfo->ts = ts;
+ return TK_NAME;
+ }
+ }
+ else {
+ int c = ls->current;
+ next(ls);
+ return c; /* single-char tokens (+ - / ...) */
+ }
+ }
+ }
+ }
+}
+
+
+void luaX_next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+void luaX_lookahead (LexState *ls) {
+ lua_assert(ls->lookahead.token == TK_EOS);
+ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+}
+
diff --git a/engines/sword25/util/lua/llex.h b/engines/sword25/util/lua/llex.h
new file mode 100644
index 0000000000..a9201cee48
--- /dev/null
+++ b/engines/sword25/util/lua/llex.h
@@ -0,0 +1,81 @@
+/*
+** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+#define FIRST_RESERVED 257
+
+/* maximum length of a reserved word */
+#define TOKEN_LEN (sizeof("function")/sizeof(char))
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+ TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+ TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
+ TK_NAME, TK_STRING, TK_EOS
+};
+
+/* number of reserved words */
+#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
+
+
+/* array with token `names' */
+LUAI_DATA const char *const luaX_tokens [];
+
+
+typedef union {
+ lua_Number r;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+typedef struct LexState {
+ int current; /* current character (charint) */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token `consumed' */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* `FuncState' is private to the parser */
+ struct lua_State *L;
+ ZIO *z; /* input stream */
+ Mbuffer *buff; /* buffer for tokens */
+ TString *source; /* current source name */
+ char decpoint; /* locale decimal point */
+} LexState;
+
+
+LUAI_FUNC void luaX_init (lua_State *L);
+LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
+ TString *source);
+LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
+LUAI_FUNC void luaX_next (LexState *ls);
+LUAI_FUNC void luaX_lookahead (LexState *ls);
+LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
+LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
+LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
+
+
+#endif
diff --git a/engines/sword25/util/lua/llimits.h b/engines/sword25/util/lua/llimits.h
new file mode 100644
index 0000000000..ca8dcb7224
--- /dev/null
+++ b/engines/sword25/util/lua/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** Limits, basic types, and some other `installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+typedef LUAI_UINT32 lu_int32;
+
+typedef LUAI_UMEM lu_mem;
+
+typedef LUAI_MEM l_mem;
+
+
+
+/* chars used as small naturals (so that `char' is reserved for characters) */
+typedef unsigned char lu_byte;
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to integer
+** this is for hashing only; there is no problem if the integer
+** cannot hold the whole pointer value
+*/
+#define IntPoint(p) ((unsigned int)(lu_mem)(p))
+
+
+
+/* type to ensure maximum alignment */
+typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
+
+
+/* result of a `usual argument conversion' over lua_Number */
+typedef LUAI_UACNUMBER l_uacNumber;
+
+
+/* internal assertions for in-house debugging */
+#ifdef lua_assert
+
+#define check_exp(c,e) (lua_assert(c), (e))
+#define api_check(l,e) lua_assert(e)
+
+#else
+
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define api_check luai_apicheck
+
+#endif
+
+
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+#ifndef cast
+#define cast(t, exp) ((t)(exp))
+#endif
+
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+typedef lu_int32 Instruction;
+
+
+
+/* maximum stack for a Lua function */
+#define MAXSTACK 250
+
+
+
+/* minimum size for the string table (must be power of 2) */
+#ifndef MINSTRTABSIZE
+#define MINSTRTABSIZE 32
+#endif
+
+
+/* minimum size for string buffer */
+#ifndef LUA_MINBUFFER
+#define LUA_MINBUFFER 32
+#endif
+
+
+#ifndef lua_lock
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+#ifndef luai_threadyield
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#ifndef HARDSTACKTESTS
+#define condhardstacktests(x) ((void)0)
+#else
+#define condhardstacktests(x) x
+#endif
+
+#endif
diff --git a/engines/sword25/util/lua/lmathlib.c b/engines/sword25/util/lua/lmathlib.c
new file mode 100644
index 0000000000..441fbf736c
--- /dev/null
+++ b/engines/sword25/util/lua/lmathlib.c
@@ -0,0 +1,263 @@
+/*
+** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $
+** Standard mathematical library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdlib.h>
+#include <math.h>
+
+#define lmathlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#undef PI
+#define PI (3.14159265358979323846)
+#define RADIANS_PER_DEGREE (PI/180.0)
+
+
+
+static int math_abs (lua_State *L) {
+ lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sin (lua_State *L) {
+ lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sinh (lua_State *L) {
+ lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cos (lua_State *L) {
+ lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cosh (lua_State *L) {
+ lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tan (lua_State *L) {
+ lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tanh (lua_State *L) {
+ lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_asin (lua_State *L) {
+ lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_acos (lua_State *L) {
+ lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan (lua_State *L) {
+ lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan2 (lua_State *L) {
+ lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_ceil (lua_State *L) {
+ lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_floor (lua_State *L) {
+ lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_fmod (lua_State *L) {
+ lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_modf (lua_State *L) {
+ double ip;
+ double fp = modf(luaL_checknumber(L, 1), &ip);
+ lua_pushnumber(L, ip);
+ lua_pushnumber(L, fp);
+ return 2;
+}
+
+static int math_sqrt (lua_State *L) {
+ lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_pow (lua_State *L) {
+ lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
+ return 1;
+}
+
+static int math_log (lua_State *L) {
+ lua_pushnumber(L, log(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_log10 (lua_State *L) {
+ lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_exp (lua_State *L) {
+ lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_deg (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);
+ return 1;
+}
+
+static int math_rad (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);
+ return 1;
+}
+
+static int math_frexp (lua_State *L) {
+ int e;
+ lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
+ lua_pushinteger(L, e);
+ return 2;
+}
+
+static int math_ldexp (lua_State *L) {
+ lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
+ return 1;
+}
+
+
+
+static int math_min (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number dmin = luaL_checknumber(L, 1);
+ int i;
+ for (i=2; i<=n; i++) {
+ lua_Number d = luaL_checknumber(L, i);
+ if (d < dmin)
+ dmin = d;
+ }
+ lua_pushnumber(L, dmin);
+ return 1;
+}
+
+
+static int math_max (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number dmax = luaL_checknumber(L, 1);
+ int i;
+ for (i=2; i<=n; i++) {
+ lua_Number d = luaL_checknumber(L, i);
+ if (d > dmax)
+ dmax = d;
+ }
+ lua_pushnumber(L, dmax);
+ return 1;
+}
+
+
+static int math_random (lua_State *L) {
+ /* the `%' avoids the (rare) case of r==1, and is needed also because on
+ some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
+ lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
+ switch (lua_gettop(L)) { /* check number of arguments */
+ case 0: { /* no arguments */
+ lua_pushnumber(L, r); /* Number between 0 and 1 */
+ break;
+ }
+ case 1: { /* only upper limit */
+ int u = luaL_checkint(L, 1);
+ luaL_argcheck(L, 1<=u, 1, "interval is empty");
+ lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
+ break;
+ }
+ case 2: { /* lower and upper limits */
+ int l = luaL_checkint(L, 1);
+ int u = luaL_checkint(L, 2);
+ luaL_argcheck(L, l<=u, 2, "interval is empty");
+ lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
+ break;
+ }
+ default: return luaL_error(L, "wrong number of arguments");
+ }
+ return 1;
+}
+
+
+static int math_randomseed (lua_State *L) {
+ srand(luaL_checkint(L, 1));
+ return 0;
+}
+
+
+static const luaL_Reg mathlib[] = {
+ {"abs", math_abs},
+ {"acos", math_acos},
+ {"asin", math_asin},
+ {"atan2", math_atan2},
+ {"atan", math_atan},
+ {"ceil", math_ceil},
+ {"cosh", math_cosh},
+ {"cos", math_cos},
+ {"deg", math_deg},
+ {"exp", math_exp},
+ {"floor", math_floor},
+ {"fmod", math_fmod},
+ {"frexp", math_frexp},
+ {"ldexp", math_ldexp},
+ {"log10", math_log10},
+ {"log", math_log},
+ {"max", math_max},
+ {"min", math_min},
+ {"modf", math_modf},
+ {"pow", math_pow},
+ {"rad", math_rad},
+ {"random", math_random},
+ {"randomseed", math_randomseed},
+ {"sinh", math_sinh},
+ {"sin", math_sin},
+ {"sqrt", math_sqrt},
+ {"tanh", math_tanh},
+ {"tan", math_tan},
+ {NULL, NULL}
+};
+
+
+/*
+** Open math library
+*/
+LUALIB_API int luaopen_math (lua_State *L) {
+ luaL_register(L, LUA_MATHLIBNAME, mathlib);
+ lua_pushnumber(L, PI);
+ lua_setfield(L, -2, "pi");
+ lua_pushnumber(L, HUGE_VAL);
+ lua_setfield(L, -2, "huge");
+#if defined(LUA_COMPAT_MOD)
+ lua_getfield(L, -1, "fmod");
+ lua_setfield(L, -2, "mod");
+#endif
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/lmem.c b/engines/sword25/util/lua/lmem.c
new file mode 100644
index 0000000000..ae7d8c965f
--- /dev/null
+++ b/engines/sword25/util/lua/lmem.c
@@ -0,0 +1,86 @@
+/*
+** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lmem_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+/*
+** About the realloc function:
+** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
+** (`osize' is the old size, `nsize' is the new size)
+**
+** Lua ensures that (ptr == NULL) iff (osize == 0).
+**
+** * frealloc(ud, NULL, 0, x) creates a new block of size `x'
+**
+** * frealloc(ud, p, x, 0) frees the block `p'
+** (in this specific case, frealloc must return NULL).
+** particularly, frealloc(ud, NULL, 0, 0) does nothing
+** (which is equivalent to free(NULL) in ANSI C)
+**
+** frealloc returns NULL if it cannot create or reallocate the area
+** (any reallocation to an equal or smaller size cannot fail!)
+*/
+
+
+
+#define MINSIZEARRAY 4
+
+
+void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
+ int limit, const char *errormsg) {
+ void *newblock;
+ int newsize;
+ if (*size >= limit/2) { /* cannot double it? */
+ if (*size >= limit) /* cannot grow even a little? */
+ luaG_runerror(L, errormsg);
+ newsize = limit; /* still have at least one free place */
+ }
+ else {
+ newsize = (*size)*2;
+ if (newsize < MINSIZEARRAY)
+ newsize = MINSIZEARRAY; /* minimum size */
+ }
+ newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
+ *size = newsize; /* update only when everything else is OK */
+ return newblock;
+}
+
+
+void *luaM_toobig (lua_State *L) {
+ luaG_runerror(L, "memory allocation error: block too big");
+ return NULL; /* to avoid warnings */
+}
+
+
+
+/*
+** generic allocation routine.
+*/
+void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ block = (*g->frealloc)(g->ud, block, osize, nsize);
+ if (block == NULL && nsize > 0)
+ luaD_throw(L, LUA_ERRMEM);
+ lua_assert((nsize == 0) == (block == NULL));
+ g->totalbytes = (g->totalbytes - osize) + nsize;
+ return block;
+}
+
diff --git a/engines/sword25/util/lua/lmem.h b/engines/sword25/util/lua/lmem.h
new file mode 100644
index 0000000000..7c2dcb3220
--- /dev/null
+++ b/engines/sword25/util/lua/lmem.h
@@ -0,0 +1,49 @@
+/*
+** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+#define MEMERRMSG "not enough memory"
+
+
+#define luaM_reallocv(L,b,on,n,e) \
+ ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \
+ luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \
+ luaM_toobig(L))
+
+#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
+#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
+#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t))
+
+#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t))
+#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
+#define luaM_newvector(L,n,t) \
+ cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
+
+#define luaM_growvector(L,v,nelems,size,t,limit,e) \
+ if ((nelems)+1 > (size)) \
+ ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
+
+#define luaM_reallocvector(L, v,oldn,n,t) \
+ ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
+
+
+LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void *luaM_toobig (lua_State *L);
+LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
+ size_t size_elem, int limit,
+ const char *errormsg);
+
+#endif
+
diff --git a/engines/sword25/util/lua/loadlib.c b/engines/sword25/util/lua/loadlib.c
new file mode 100644
index 0000000000..d955f3ef41
--- /dev/null
+++ b/engines/sword25/util/lua/loadlib.c
@@ -0,0 +1,664 @@
+/*
+** $Id: loadlib.c,v 1.52.1.2 2007/12/28 14:58:43 roberto Exp $
+** Dynamic library loader for Lua
+** See Copyright Notice in lua.h
+**
+** This module contains an implementation of loadlib for Unix systems
+** that have dlfcn, an implementation for Darwin (Mac OS X), an
+** implementation for Windows, and a stub for other systems.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#define loadlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/* prefix for open functions in C libraries */
+#define LUA_POF "luaopen_"
+
+/* separator for open functions in C libraries */
+#define LUA_OFSEP "_"
+
+
+#define LIBPREFIX "LOADLIB: "
+
+#define POF LUA_POF
+#define LIB_FAIL "open"
+
+
+/* error codes for ll_loadfunc */
+#define ERRLIB 1
+#define ERRFUNC 2
+
+#define setprogdir(L) ((void)0)
+
+
+static void ll_unloadlib (void *lib);
+static void *ll_load (lua_State *L, const char *path);
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
+
+
+
+#if defined(LUA_DL_DLOPEN)
+/*
+** {========================================================================
+** This is an implementation of loadlib based on the dlfcn interface.
+** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
+** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
+** as an emulation layer on top of native functions.
+** =========================================================================
+*/
+
+#include <dlfcn.h>
+
+static void ll_unloadlib (void *lib) {
+ dlclose(lib);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ void *lib = dlopen(path, RTLD_NOW);
+ if (lib == NULL) lua_pushstring(L, dlerror());
+ return lib;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
+ if (f == NULL) lua_pushstring(L, dlerror());
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DLL)
+/*
+** {======================================================================
+** This is an implementation of loadlib for Windows using native functions.
+** =======================================================================
+*/
+
+#include <windows.h>
+
+
+#undef setprogdir
+
+static void setprogdir (lua_State *L) {
+ char buff[MAX_PATH + 1];
+ char *lb;
+ DWORD nsize = sizeof(buff)/sizeof(char);
+ DWORD n = GetModuleFileNameA(NULL, buff, nsize);
+ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
+ luaL_error(L, "unable to get ModuleFileName");
+ else {
+ *lb = '\0';
+ luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
+ lua_remove(L, -2); /* remove original string */
+ }
+}
+
+
+static void pusherror (lua_State *L) {
+ int error = GetLastError();
+ char buffer[128];
+ if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error, 0, buffer, sizeof(buffer), NULL))
+ lua_pushstring(L, buffer);
+ else
+ lua_pushfstring(L, "system error %d\n", error);
+}
+
+static void ll_unloadlib (void *lib) {
+ FreeLibrary((HINSTANCE)lib);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ HINSTANCE lib = LoadLibraryA(path);
+ if (lib == NULL) pusherror(L);
+ return lib;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
+ if (f == NULL) pusherror(L);
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DYLD)
+/*
+** {======================================================================
+** Native Mac OS X / Darwin Implementation
+** =======================================================================
+*/
+
+#include <mach-o/dyld.h>
+
+
+/* Mac appends a `_' before C function names */
+#undef POF
+#define POF "_" LUA_POF
+
+
+static void pusherror (lua_State *L) {
+ const char *err_str;
+ const char *err_file;
+ NSLinkEditErrors err;
+ int err_num;
+ NSLinkEditError(&err, &err_num, &err_file, &err_str);
+ lua_pushstring(L, err_str);
+}
+
+
+static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
+ switch (ret) {
+ case NSObjectFileImageInappropriateFile:
+ return "file is not a bundle";
+ case NSObjectFileImageArch:
+ return "library is for wrong CPU type";
+ case NSObjectFileImageFormat:
+ return "bad format";
+ case NSObjectFileImageAccess:
+ return "cannot access file";
+ case NSObjectFileImageFailure:
+ default:
+ return "unable to load library";
+ }
+}
+
+
+static void ll_unloadlib (void *lib) {
+ NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ NSObjectFileImage img;
+ NSObjectFileImageReturnCode ret;
+ /* this would be a rare case, but prevents crashing if it happens */
+ if(!_dyld_present()) {
+ lua_pushliteral(L, "dyld not present");
+ return NULL;
+ }
+ ret = NSCreateObjectFileImageFromFile(path, &img);
+ if (ret == NSObjectFileImageSuccess) {
+ NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
+ NSLINKMODULE_OPTION_RETURN_ON_ERROR);
+ NSDestroyObjectFileImage(img);
+ if (mod == NULL) pusherror(L);
+ return mod;
+ }
+ lua_pushstring(L, errorfromcode(ret));
+ return NULL;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
+ if (nss == NULL) {
+ lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
+ return NULL;
+ }
+ return (lua_CFunction)NSAddressOfSymbol(nss);
+}
+
+/* }====================================================== */
+
+
+
+#else
+/*
+** {======================================================
+** Fallback for other systems
+** =======================================================
+*/
+
+#undef LIB_FAIL
+#define LIB_FAIL "absent"
+
+
+#define DLMSG "dynamic libraries not enabled; check your Lua installation"
+
+
+static void ll_unloadlib (void *lib) {
+ (void)lib; /* to avoid warnings */
+}
+
+
+static void *ll_load (lua_State *L, const char *path) {
+ (void)path; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+
+static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
+ (void)lib; (void)sym; /* to avoid warnings */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+/* }====================================================== */
+#endif
+
+
+
+static void **ll_register (lua_State *L, const char *path) {
+ void **plib;
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
+ if (!lua_isnil(L, -1)) /* is there an entry? */
+ plib = (void **)lua_touserdata(L, -1);
+ else { /* no entry yet; create one */
+ lua_pop(L, 1);
+ plib = (void **)lua_newuserdata(L, sizeof(const void *));
+ *plib = NULL;
+ luaL_getmetatable(L, "_LOADLIB");
+ lua_setmetatable(L, -2);
+ lua_pushfstring(L, "%s%s", LIBPREFIX, path);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+ return plib;
+}
+
+
+/*
+** __gc tag method: calls library's `ll_unloadlib' function with the lib
+** handle
+*/
+static int gctm (lua_State *L) {
+ void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
+ if (*lib) ll_unloadlib(*lib);
+ *lib = NULL; /* mark library as closed */
+ return 0;
+}
+
+
+static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
+ void **reg = ll_register(L, path);
+ if (*reg == NULL) *reg = ll_load(L, path);
+ if (*reg == NULL)
+ return ERRLIB; /* unable to load library */
+ else {
+ lua_CFunction f = ll_sym(L, *reg, sym);
+ if (f == NULL)
+ return ERRFUNC; /* unable to find function */
+ lua_pushcfunction(L, f);
+ return 0; /* return function */
+ }
+}
+
+
+static int ll_loadlib (lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ const char *init = luaL_checkstring(L, 2);
+ int stat = ll_loadfunc(L, path, init);
+ if (stat == 0) /* no errors? */
+ return 1; /* return the loaded function */
+ else { /* error; error message is on stack top */
+ lua_pushnil(L);
+ lua_insert(L, -2);
+ lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
+ return 3; /* return nil, error message, and where */
+ }
+}
+
+
+
+/*
+** {======================================================
+** 'require' function
+** =======================================================
+*/
+
+
+static int readable (const char *filename) {
+ FILE *f = fopen(filename, "r"); /* try to open file */
+ if (f == NULL) return 0; /* open failed */
+ fclose(f);
+ return 1;
+}
+
+
+static const char *pushnexttemplate (lua_State *L, const char *path) {
+ const char *l;
+ while (*path == *LUA_PATHSEP) path++; /* skip separators */
+ if (*path == '\0') return NULL; /* no more templates */
+ l = strchr(path, *LUA_PATHSEP); /* find next separator */
+ if (l == NULL) l = path + strlen(path);
+ lua_pushlstring(L, path, l - path); /* template */
+ return l;
+}
+
+
+static const char *findfile (lua_State *L, const char *name,
+ const char *pname) {
+ const char *path;
+ name = luaL_gsub(L, name, ".", LUA_DIRSEP);
+ lua_getfield(L, LUA_ENVIRONINDEX, pname);
+ path = lua_tostring(L, -1);
+ if (path == NULL)
+ luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
+ lua_pushliteral(L, ""); /* error accumulator */
+ while ((path = pushnexttemplate(L, path)) != NULL) {
+ const char *filename;
+ filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
+ lua_remove(L, -2); /* remove path template */
+ if (readable(filename)) /* does file exist and is readable? */
+ return filename; /* return that file name */
+ lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
+ lua_remove(L, -2); /* remove file name */
+ lua_concat(L, 2); /* add entry to possible error message */
+ }
+ return NULL; /* not found */
+}
+
+
+static void loaderror (lua_State *L, const char *filename) {
+ luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
+ lua_tostring(L, 1), filename, lua_tostring(L, -1));
+}
+
+
+static int loader_Lua (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ filename = findfile(L, name, "path");
+ if (filename == NULL) return 1; /* library not found in this path */
+ if (luaL_loadfile(L, filename) != 0)
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static const char *mkfuncname (lua_State *L, const char *modname) {
+ const char *funcname;
+ const char *mark = strchr(modname, *LUA_IGMARK);
+ if (mark) modname = mark + 1;
+ funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
+ funcname = lua_pushfstring(L, POF"%s", funcname);
+ lua_remove(L, -2); /* remove 'gsub' result */
+ return funcname;
+}
+
+
+static int loader_C (lua_State *L) {
+ const char *funcname;
+ const char *name = luaL_checkstring(L, 1);
+ const char *filename = findfile(L, name, "cpath");
+ if (filename == NULL) return 1; /* library not found in this path */
+ funcname = mkfuncname(L, name);
+ if (ll_loadfunc(L, filename, funcname) != 0)
+ loaderror(L, filename);
+ return 1; /* library loaded successfully */
+}
+
+
+static int loader_Croot (lua_State *L) {
+ const char *funcname;
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ const char *p = strchr(name, '.');
+ int stat;
+ if (p == NULL) return 0; /* is root */
+ lua_pushlstring(L, name, p - name);
+ filename = findfile(L, lua_tostring(L, -1), "cpath");
+ if (filename == NULL) return 1; /* root not found */
+ funcname = mkfuncname(L, name);
+ if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
+ if (stat != ERRFUNC) loaderror(L, filename); /* real error */
+ lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
+ name, filename);
+ return 1; /* function not found */
+ }
+ return 1;
+}
+
+
+static int loader_preload (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_getfield(L, LUA_ENVIRONINDEX, "preload");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.preload") " must be a table");
+ lua_getfield(L, -1, name);
+ if (lua_isnil(L, -1)) /* not found? */
+ lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
+ return 1;
+}
+
+
+static const int sentinel_ = 0;
+#define sentinel ((void *)&sentinel_)
+
+
+static int ll_require (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ int i;
+ lua_settop(L, 1); /* _LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, 2, name);
+ if (lua_toboolean(L, -1)) { /* is it there? */
+ if (lua_touserdata(L, -1) == sentinel) /* check loops */
+ luaL_error(L, "loop or previous error loading module " LUA_QS, name);
+ return 1; /* package is already loaded */
+ }
+ /* else must load it; iterate over available loaders */
+ lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
+ if (!lua_istable(L, -1))
+ luaL_error(L, LUA_QL("package.loaders") " must be a table");
+ lua_pushliteral(L, ""); /* error message accumulator */
+ for (i=1; ; i++) {
+ lua_rawgeti(L, -2, i); /* get a loader */
+ if (lua_isnil(L, -1))
+ luaL_error(L, "module " LUA_QS " not found:%s",
+ name, lua_tostring(L, -2));
+ lua_pushstring(L, name);
+ lua_call(L, 1, 1); /* call it */
+ if (lua_isfunction(L, -1)) /* did it find module? */
+ break; /* module loaded successfully */
+ else if (lua_isstring(L, -1)) /* loader returned error message? */
+ lua_concat(L, 2); /* accumulate it */
+ else
+ lua_pop(L, 1);
+ }
+ lua_pushlightuserdata(L, sentinel);
+ lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
+ lua_pushstring(L, name); /* pass name as argument to module */
+ lua_call(L, 1, 1); /* run loaded module */
+ if (!lua_isnil(L, -1)) /* non-nil return? */
+ lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
+ lua_getfield(L, 2, name);
+ if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
+ lua_pushboolean(L, 1); /* use true as result */
+ lua_pushvalue(L, -1); /* extra copy to be returned */
+ lua_setfield(L, 2, name); /* _LOADED[name] = true */
+ }
+ return 1;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** 'module' function
+** =======================================================
+*/
+
+
+static void setfenv (lua_State *L) {
+ lua_Debug ar;
+ lua_getstack(L, 1, &ar);
+ lua_getinfo(L, "f", &ar);
+ lua_pushvalue(L, -2);
+ lua_setfenv(L, -2);
+ lua_pop(L, 1);
+}
+
+
+static void dooptions (lua_State *L, int n) {
+ int i;
+ for (i = 2; i <= n; i++) {
+ lua_pushvalue(L, i); /* get option (a function) */
+ lua_pushvalue(L, -2); /* module */
+ lua_call(L, 1, 0);
+ }
+}
+
+
+static void modinit (lua_State *L, const char *modname) {
+ const char *dot;
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "_M"); /* module._M = module */
+ lua_pushstring(L, modname);
+ lua_setfield(L, -2, "_NAME");
+ dot = strrchr(modname, '.'); /* look for last dot in module name */
+ if (dot == NULL) dot = modname;
+ else dot++;
+ /* set _PACKAGE as package name (full module name minus last part) */
+ lua_pushlstring(L, modname, dot - modname);
+ lua_setfield(L, -2, "_PACKAGE");
+}
+
+
+static int ll_module (lua_State *L) {
+ const char *modname = luaL_checkstring(L, 1);
+ int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
+ if (!lua_istable(L, -1)) { /* not found? */
+ lua_pop(L, 1); /* remove previous result */
+ /* try global variable (and create one if it does not exist) */
+ if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
+ return luaL_error(L, "name conflict for module " LUA_QS, modname);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
+ }
+ /* check whether table already has a _NAME field */
+ lua_getfield(L, -1, "_NAME");
+ if (!lua_isnil(L, -1)) /* is table an initialized module? */
+ lua_pop(L, 1);
+ else { /* no; initialize it */
+ lua_pop(L, 1);
+ modinit(L, modname);
+ }
+ lua_pushvalue(L, -1);
+ setfenv(L);
+ dooptions(L, loaded - 1);
+ return 0;
+}
+
+
+static int ll_seeall (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ if (!lua_getmetatable(L, 1)) {
+ lua_createtable(L, 0, 1); /* create new metatable */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, 1);
+ }
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_setfield(L, -2, "__index"); /* mt.__index = _G */
+ return 0;
+}
+
+
+/* }====================================================== */
+
+
+
+/* auxiliary mark (for internal use) */
+#define AUXMARK "\1"
+
+static void setpath (lua_State *L, const char *fieldname, const char *envname,
+ const char *def) {
+ const char *path = getenv(envname);
+ if (path == NULL) /* no environment variable? */
+ lua_pushstring(L, def); /* use default */
+ else {
+ /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
+ path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
+ LUA_PATHSEP AUXMARK LUA_PATHSEP);
+ luaL_gsub(L, path, AUXMARK, def);
+ lua_remove(L, -2);
+ }
+ setprogdir(L);
+ lua_setfield(L, -2, fieldname);
+}
+
+
+static const luaL_Reg pk_funcs[] = {
+ {"loadlib", ll_loadlib},
+ {"seeall", ll_seeall},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+ {"module", ll_module},
+ {"require", ll_require},
+ {NULL, NULL}
+};
+
+
+static const lua_CFunction loaders[] =
+ {loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
+
+
+LUALIB_API int luaopen_package (lua_State *L) {
+ int i;
+ /* create new type _LOADLIB */
+ luaL_newmetatable(L, "_LOADLIB");
+ lua_pushcfunction(L, gctm);
+ lua_setfield(L, -2, "__gc");
+ /* create `package' table */
+ luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
+#if defined(LUA_COMPAT_LOADLIB)
+ lua_getfield(L, -1, "loadlib");
+ lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
+#endif
+ lua_pushvalue(L, -1);
+ lua_replace(L, LUA_ENVIRONINDEX);
+ /* create `loaders' table */
+ lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
+ /* fill it with pre-defined loaders */
+ for (i=0; loaders[i] != NULL; i++) {
+ lua_pushcfunction(L, loaders[i]);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
+ setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
+ setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
+ /* store config information */
+ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
+ LUA_EXECDIR "\n" LUA_IGMARK);
+ lua_setfield(L, -2, "config");
+ /* set field `loaded' */
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
+ lua_setfield(L, -2, "loaded");
+ /* set field `preload' */
+ lua_newtable(L);
+ lua_setfield(L, -2, "preload");
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ luaL_register(L, NULL, ll_funcs); /* open lib into global table */
+ lua_pop(L, 1);
+ return 1; /* return 'package' table */
+}
+
diff --git a/engines/sword25/util/lua/lobject.c b/engines/sword25/util/lua/lobject.c
new file mode 100644
index 0000000000..4ff50732a4
--- /dev/null
+++ b/engines/sword25/util/lua/lobject.c
@@ -0,0 +1,214 @@
+/*
+** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lobject_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lvm.h"
+
+
+
+const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
+
+
+/*
+** converts an integer to a "floating point byte", represented as
+** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
+** eeeee != 0 and (xxx) otherwise.
+*/
+int luaO_int2fb (unsigned int x) {
+ int e = 0; /* expoent */
+ while (x >= 16) {
+ x = (x+1) >> 1;
+ e++;
+ }
+ if (x < 8) return x;
+ else return ((e+1) << 3) | (cast_int(x) - 8);
+}
+
+
+/* converts back */
+int luaO_fb2int (int x) {
+ int e = (x >> 3) & 31;
+ if (e == 0) return x;
+ else return ((x & 7)+8) << (e - 1);
+}
+
+
+int luaO_log2 (unsigned int x) {
+ static const lu_byte log_2[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,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,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = -1;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+
+}
+
+
+int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
+ if (ttype(t1) != ttype(t2)) return 0;
+ else switch (ttype(t1)) {
+ case LUA_TNIL:
+ return 1;
+ case LUA_TNUMBER:
+ return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN:
+ return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
+ case LUA_TLIGHTUSERDATA:
+ return pvalue(t1) == pvalue(t2);
+ default:
+ lua_assert(iscollectable(t1));
+ return gcvalue(t1) == gcvalue(t2);
+ }
+}
+
+
+int luaO_str2d (const char *s, lua_Number *result) {
+ char *endptr;
+ *result = lua_str2number(s, &endptr);
+ if (endptr == s) return 0; /* conversion failed */
+ if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
+ *result = cast_num(strtoul(s, &endptr, 16));
+ if (*endptr == '\0') return 1; /* most common case */
+ while (isspace(cast(unsigned char, *endptr))) endptr++;
+ if (*endptr != '\0') return 0; /* invalid trailing characters? */
+ return 1;
+}
+
+
+
+static void pushstr (lua_State *L, const char *str) {
+ setsvalue2s(L, L->top, luaS_new(L, str));
+ incr_top(L);
+}
+
+
+/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
+const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
+ int n = 1;
+ pushstr(L, "");
+ for (;;) {
+ const char *e = strchr(fmt, '%');
+ if (e == NULL) break;
+ setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
+ incr_top(L);
+ switch (*(e+1)) {
+ case 's': {
+ const char *s = va_arg(argp, char *);
+ if (s == NULL) s = "(null)";
+ pushstr(L, s);
+ break;
+ }
+ case 'c': {
+ char buff[2];
+ buff[0] = cast(char, va_arg(argp, int));
+ buff[1] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ case 'd': {
+ setnvalue(L->top, cast_num(va_arg(argp, int)));
+ incr_top(L);
+ break;
+ }
+ case 'f': {
+ setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));
+ incr_top(L);
+ break;
+ }
+ case 'p': {
+ char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
+ sprintf(buff, "%p", va_arg(argp, void *));
+ pushstr(L, buff);
+ break;
+ }
+ case '%': {
+ pushstr(L, "%");
+ break;
+ }
+ default: {
+ char buff[3];
+ buff[0] = '%';
+ buff[1] = *(e+1);
+ buff[2] = '\0';
+ pushstr(L, buff);
+ break;
+ }
+ }
+ n += 2;
+ fmt = e+2;
+ }
+ pushstr(L, fmt);
+ luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);
+ L->top -= n;
+ return svalue(L->top - 1);
+}
+
+
+const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *msg;
+ va_list argp;
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ return msg;
+}
+
+
+void luaO_chunkid (char *out, const char *source, size_t bufflen) {
+ if (*source == '=') {
+ strncpy(out, source+1, bufflen); /* remove first char */
+ out[bufflen-1] = '\0'; /* ensures null termination */
+ }
+ else { /* out = "source", or "...source" */
+ if (*source == '@') {
+ size_t l;
+ source++; /* skip the `@' */
+ bufflen -= sizeof(" '...' ");
+ l = strlen(source);
+ strcpy(out, "");
+ if (l > bufflen) {
+ source += (l-bufflen); /* get last part of file name */
+ strcat(out, "...");
+ }
+ strcat(out, source);
+ }
+ else { /* out = [string "string"] */
+ size_t len = strcspn(source, "\n\r"); /* stop at first newline */
+ bufflen -= sizeof(" [string \"...\"] ");
+ if (len > bufflen) len = bufflen;
+ strcpy(out, "[string \"");
+ if (source[len] != '\0') { /* must truncate? */
+ strncat(out, source, len);
+ strcat(out, "...");
+ }
+ else
+ strcat(out, source);
+ strcat(out, "\"]");
+ }
+ }
+}
diff --git a/engines/sword25/util/lua/lobject.h b/engines/sword25/util/lua/lobject.h
new file mode 100644
index 0000000000..e7199dfc68
--- /dev/null
+++ b/engines/sword25/util/lua/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/* tags for values visible from Lua */
+#define LAST_TAG LUA_TTHREAD
+
+#define NUM_TAGS (LAST_TAG+1)
+
+
+/*
+** Extra tags for non-values
+*/
+#define LUA_TPROTO (LAST_TAG+1)
+#define LUA_TUPVAL (LAST_TAG+2)
+#define LUA_TDEADKEY (LAST_TAG+3)
+
+
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
+
+
+/*
+** Common header in struct form
+*/
+typedef struct GCheader {
+ CommonHeader;
+} GCheader;
+
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union {
+ GCObject *gc;
+ void *p;
+ lua_Number n;
+ int b;
+} Value;
+
+
+/*
+** Tagged Values
+*/
+
+#define TValuefields Value value; int tt
+
+typedef struct lua_TValue {
+ TValuefields;
+} TValue;
+
+
+/* Macros to test type */
+#define ttisnil(o) (ttype(o) == LUA_TNIL)
+#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
+#define ttisstring(o) (ttype(o) == LUA_TSTRING)
+#define ttistable(o) (ttype(o) == LUA_TTABLE)
+#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
+#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
+#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
+#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
+
+/* Macros to access values */
+#define ttype(o) ((o)->tt)
+#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
+#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
+#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
+#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
+#define tsvalue(o) (&rawtsvalue(o)->tsv)
+#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
+#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
+#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
+
+#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
+
+/*
+** for internal debug only
+*/
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
+
+
+/* Macros to set values */
+#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
+
+#define setnvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
+
+#define setpvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
+
+#define setbvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
+
+#define setsvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; o1->tt=o2->tt; \
+ checkliveness(G(L),o1); }
+
+
+/*
+** different types of sets, according to destination
+*/
+
+/* from stack to (same) stack */
+#define setobjs2s setobj
+/* to stack (not from same stack) */
+#define setobj2s setobj
+#define setsvalue2s setsvalue
+#define sethvalue2s sethvalue
+#define setptvalue2s setptvalue
+/* from table to same table */
+#define setobjt2t setobj
+/* to table */
+#define setobj2t setobj
+/* to new object */
+#define setobj2n setobj
+#define setsvalue2n setsvalue
+
+#define setttype(obj, tt) (ttype(obj) = (tt))
+
+
+#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
+
+
+
+typedef TValue *StkId; /* index to stack elements */
+
+
+/*
+** String headers for string table
+*/
+typedef union TString {
+ L_Umaxalign dummy; /* ensures maximum alignment for strings */
+ struct {
+ CommonHeader;
+ lu_byte reserved;
+ unsigned int hash;
+ size_t len;
+ } tsv;
+} TString;
+
+
+#define getstr(ts) cast(const char *, (ts) + 1)
+#define svalue(o) getstr(tsvalue(o))
+
+
+
+typedef union Udata {
+ L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+ struct {
+ CommonHeader;
+ struct Table *metatable;
+ struct Table *env;
+ size_t len;
+ } uv;
+} Udata;
+
+
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ TValue *k; /* constants used by the function */
+ Instruction *code;
+ struct Proto **p; /* functions defined inside the function */
+ int *lineinfo; /* map from opcodes to source lines */
+ struct LocVar *locvars; /* information about local variables */
+ TString **upvalues; /* upvalue names */
+ TString *source;
+ int sizeupvalues;
+ int sizek; /* size of `k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of `p' */
+ int sizelocvars;
+ int linedefined;
+ int lastlinedefined;
+ GCObject *gclist;
+ lu_byte nups; /* number of upvalues */
+ lu_byte numparams;
+ lu_byte is_vararg;
+ lu_byte maxstacksize;
+} Proto;
+
+
+/* masks for new-style vararg */
+#define VARARG_HASARG 1
+#define VARARG_ISVARARG 2
+#define VARARG_NEEDSARG 4
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+
+/*
+** Upvalues
+*/
+
+typedef struct UpVal {
+ CommonHeader;
+ TValue *v; /* points to stack or to its own value */
+ union {
+ TValue value; /* the value (when closed) */
+ struct { /* double linked list (when open) */
+ struct UpVal *prev;
+ struct UpVal *next;
+ } l;
+ } u;
+} UpVal;
+
+
+/*
+** Closures
+*/
+
+#define ClosureHeader \
+ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
+ struct Table *env
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1];
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1];
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
+#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
+
+
+/*
+** Tables
+*/
+
+typedef union TKey {
+ struct {
+ TValuefields;
+ struct Node *next; /* for chaining */
+ } nk;
+ TValue tvk;
+} TKey;
+
+
+typedef struct Node {
+ TValue i_val;
+ TKey i_key;
+} Node;
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of `node' array */
+ struct Table *metatable;
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ GCObject *gclist;
+ int sizearray; /* size of `array' array */
+} Table;
+
+
+
+/*
+** `module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+#define luaO_nilobject (&luaO_nilobject_)
+
+LUAI_DATA const TValue luaO_nilobject_;
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+LUAI_FUNC int luaO_log2 (unsigned int x);
+LUAI_FUNC int luaO_int2fb (unsigned int x);
+LUAI_FUNC int luaO_fb2int (int x);
+LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
+
+
+#endif
+
diff --git a/engines/sword25/util/lua/lopcodes.c b/engines/sword25/util/lua/lopcodes.c
new file mode 100644
index 0000000000..4cc745230b
--- /dev/null
+++ b/engines/sword25/util/lua/lopcodes.c
@@ -0,0 +1,102 @@
+/*
+** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** See Copyright Notice in lua.h
+*/
+
+
+#define lopcodes_c
+#define LUA_CORE
+
+
+#include "lopcodes.h"
+
+
+/* ORDER OP */
+
+const char *const luaP_opnames[NUM_OPCODES+1] = {
+ "MOVE",
+ "LOADK",
+ "LOADBOOL",
+ "LOADNIL",
+ "GETUPVAL",
+ "GETGLOBAL",
+ "GETTABLE",
+ "SETGLOBAL",
+ "SETUPVAL",
+ "SETTABLE",
+ "NEWTABLE",
+ "SELF",
+ "ADD",
+ "SUB",
+ "MUL",
+ "DIV",
+ "MOD",
+ "POW",
+ "UNM",
+ "NOT",
+ "LEN",
+ "CONCAT",
+ "JMP",
+ "EQ",
+ "LT",
+ "LE",
+ "TEST",
+ "TESTSET",
+ "CALL",
+ "TAILCALL",
+ "RETURN",
+ "FORLOOP",
+ "FORPREP",
+ "TFORLOOP",
+ "SETLIST",
+ "CLOSE",
+ "CLOSURE",
+ "VARARG",
+ NULL
+};
+
+
+#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
+
+const lu_byte luaP_opmodes[NUM_OPCODES] = {
+/* T A B C mode opcode */
+ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
+ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
+ ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
+ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
+ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
+ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
+ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
+ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */
+ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
+ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
+ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
+ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
+ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
+ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
+};
+
diff --git a/engines/sword25/util/lua/lopcodes.h b/engines/sword25/util/lua/lopcodes.h
new file mode 100644
index 0000000000..41224d6ee1
--- /dev/null
+++ b/engines/sword25/util/lua/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits.
+ Instructions can have the following fields:
+ `A' : 8 bits
+ `B' : 9 bits
+ `C' : 9 bits
+ `Bx' : 18 bits (`B' and `C' together)
+ `sBx' : signed Bx
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 9
+#define SIZE_B 9
+#define SIZE_Bx (SIZE_C + SIZE_B)
+#define SIZE_A 8
+
+#define SIZE_OP 6
+
+#define POS_OP 0
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_C (POS_A + SIZE_A)
+#define POS_B (POS_C + SIZE_C)
+#define POS_Bx POS_C
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+*/
+#if SIZE_Bx < LUAI_BITSINT-1
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
+#else
+#define MAXARG_Bx MAX_INT
+#define MAXARG_sBx MAX_INT
+#endif
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
+#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
+
+#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
+
+#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
+#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
+ ((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
+
+#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
+#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
+ ((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
+
+#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
+
+
+#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+
+/*
+** Macros to operate RK indices
+*/
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x) ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r) ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x) ((x) | BITRK)
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R(x) - register
+** Kst(x) - constant (in constant table)
+** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+*/
+
+
+/*
+** grep "ORDER OP" if you change these enums
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R(A) := R(B) */
+OP_LOADK,/* A Bx R(A) := Kst(Bx) */
+OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
+OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
+OP_GETUPVAL,/* A B R(A) := UpValue[B] */
+
+OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
+OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
+
+OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
+OP_SETUPVAL,/* A B UpValue[B] := R(A) */
+OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
+
+OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
+
+OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
+
+OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
+OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
+OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
+OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
+OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
+OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
+OP_UNM,/* A B R(A) := -R(B) */
+OP_NOT,/* A B R(A) := not R(B) */
+OP_LEN,/* A B R(A) := length of R(B) */
+
+OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
+
+OP_JMP,/* sBx pc+=sBx */
+
+OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
+OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
+OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
+
+OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
+OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
+
+OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
+
+OP_FORLOOP,/* A sBx R(A)+=R(A+2);
+ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
+
+OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+ if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
+OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
+
+OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
+OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
+
+OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
+} OpCode;
+
+
+#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+ (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
+ and can be 0: OP_CALL then sets `top' to last_result+1, so
+ next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
+
+ (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to `top'
+
+ (*) In OP_SETLIST, if (B == 0) then B = `top';
+ if (C == 0) then next `instruction' is real C
+
+ (*) For comparisons, A specifies what condition the test should accept
+ (true or false).
+
+ (*) All `skips' (pc++) assume that next instruction is a jump
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-1: op mode
+** bits 2-3: C arg mode
+** bits 4-5: B arg mode
+** bit 6: instruction set register A
+** bit 7: operator is a test
+*/
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
+#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
+#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
+
+
+LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+
+#endif
diff --git a/engines/sword25/util/lua/loslib.c b/engines/sword25/util/lua/loslib.c
new file mode 100644
index 0000000000..da06a572ac
--- /dev/null
+++ b/engines/sword25/util/lua/loslib.c
@@ -0,0 +1,243 @@
+/*
+** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static int os_pushresult (lua_State *L, int i, const char *filename) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (i) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ lua_pushfstring(L, "%s: %s", filename, strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+static int os_execute (lua_State *L) {
+ lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
+ return 1;
+}
+
+
+static int os_remove (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ return os_pushresult(L, remove(filename) == 0, filename);
+}
+
+
+static int os_rename (lua_State *L) {
+ const char *fromname = luaL_checkstring(L, 1);
+ const char *toname = luaL_checkstring(L, 2);
+ return os_pushresult(L, rename(fromname, toname) == 0, fromname);
+}
+
+
+static int os_tmpname (lua_State *L) {
+ char buff[LUA_TMPNAMBUFSIZE];
+ int err;
+ lua_tmpnam(buff, err);
+ if (err)
+ return luaL_error(L, "unable to generate a unique filename");
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int os_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int os_clock (lua_State *L) {
+ lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+** wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+static void setfield (lua_State *L, const char *key, int value) {
+ lua_pushinteger(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+ if (value < 0) /* undefined? */
+ return; /* does not set field */
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+static int getboolfield (lua_State *L, const char *key) {
+ int res;
+ lua_getfield(L, -1, key);
+ res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d) {
+ int res;
+ lua_getfield(L, -1, key);
+ if (lua_isnumber(L, -1))
+ res = (int)lua_tointeger(L, -1);
+ else {
+ if (d < 0)
+ return luaL_error(L, "field " LUA_QS " missing in date table", key);
+ res = d;
+ }
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int os_date (lua_State *L) {
+ const char *s = luaL_optstring(L, 1, "%c");
+ time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
+ struct tm *stm;
+ if (*s == '!') { /* UTC? */
+ stm = gmtime(&t);
+ s++; /* skip `!' */
+ }
+ else
+ stm = localtime(&t);
+ if (stm == NULL) /* invalid date? */
+ lua_pushnil(L);
+ else if (strcmp(s, "*t") == 0) {
+ lua_createtable(L, 0, 9); /* 9 = number of fields */
+ setfield(L, "sec", stm->tm_sec);
+ setfield(L, "min", stm->tm_min);
+ setfield(L, "hour", stm->tm_hour);
+ setfield(L, "day", stm->tm_mday);
+ setfield(L, "month", stm->tm_mon+1);
+ setfield(L, "year", stm->tm_year+1900);
+ setfield(L, "wday", stm->tm_wday+1);
+ setfield(L, "yday", stm->tm_yday+1);
+ setboolfield(L, "isdst", stm->tm_isdst);
+ }
+ else {
+ char cc[3];
+ luaL_Buffer b;
+ cc[0] = '%'; cc[2] = '\0';
+ luaL_buffinit(L, &b);
+ for (; *s; s++) {
+ if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
+ luaL_addchar(&b, *s);
+ else {
+ size_t reslen;
+ char buff[200]; /* should be big enough for any conversion result */
+ cc[1] = *(++s);
+ reslen = strftime(buff, sizeof(buff), cc, stm);
+ luaL_addlstring(&b, buff, reslen);
+ }
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+static int os_time (lua_State *L) {
+ time_t t;
+ if (lua_isnoneornil(L, 1)) /* called without args? */
+ t = time(NULL); /* get current time */
+ else {
+ struct tm ts;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1); /* make sure table is at the top */
+ ts.tm_sec = getfield(L, "sec", 0);
+ ts.tm_min = getfield(L, "min", 0);
+ ts.tm_hour = getfield(L, "hour", 12);
+ ts.tm_mday = getfield(L, "day", -1);
+ ts.tm_mon = getfield(L, "month", -1) - 1;
+ ts.tm_year = getfield(L, "year", -1) - 1900;
+ ts.tm_isdst = getboolfield(L, "isdst");
+ t = mktime(&ts);
+ }
+ if (t == (time_t)(-1))
+ lua_pushnil(L);
+ else
+ lua_pushnumber(L, (lua_Number)t);
+ return 1;
+}
+
+
+static int os_difftime (lua_State *L) {
+ lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
+ (time_t)(luaL_optnumber(L, 2, 0))));
+ return 1;
+}
+
+/* }====================================================== */
+
+
+static int os_setlocale (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ const char *l = luaL_optstring(L, 1, NULL);
+ int op = luaL_checkoption(L, 2, "all", catnames);
+ lua_pushstring(L, setlocale(cat[op], l));
+ return 1;
+}
+
+
+static int os_exit (lua_State *L) {
+ exit(luaL_optint(L, 1, EXIT_SUCCESS));
+}
+
+static const luaL_Reg syslib[] = {
+ {"clock", os_clock},
+ {"date", os_date},
+ {"difftime", os_difftime},
+ {"execute", os_execute},
+ {"exit", os_exit},
+ {"getenv", os_getenv},
+ {"remove", os_remove},
+ {"rename", os_rename},
+ {"setlocale", os_setlocale},
+ {"time", os_time},
+ {"tmpname", os_tmpname},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaopen_os (lua_State *L) {
+ luaL_register(L, LUA_OSLIBNAME, syslib);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/lparser.c b/engines/sword25/util/lua/lparser.c
new file mode 100644
index 0000000000..1e2a9a88b7
--- /dev/null
+++ b/engines/sword25/util/lua/lparser.c
@@ -0,0 +1,1339 @@
+/*
+** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lparser_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+
+#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
+
+#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]])
+
+#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m)
+
+
+/*
+** nodes for block list (list of active blocks)
+*/
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
+ int breaklist; /* list of jumps out of this loop */
+ lu_byte nactvar; /* # active locals outside the breakable structure */
+ lu_byte upval; /* true if some variable in the block is an upvalue */
+ lu_byte isbreakable; /* true if `block' is a loop */
+} BlockCnt;
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void chunk (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+
+
+static void anchor_token (LexState *ls) {
+ if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {
+ TString *ts = ls->t.seminfo.ts;
+ luaX_newstring(ls, getstr(ts), ts->tsv.len);
+ }
+}
+
+
+static void error_expected (LexState *ls, int token) {
+ luaX_syntaxerror(ls,
+ luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token)));
+}
+
+
+static void errorlimit (FuncState *fs, int limit, const char *what) {
+ const char *msg = (fs->f->linedefined == 0) ?
+ luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) :
+ luaO_pushfstring(fs->L, "function at line %d has more than %d %s",
+ fs->f->linedefined, limit, what);
+ luaX_lexerror(fs->ls, msg, 0);
+}
+
+
+static int testnext (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ luaX_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+}
+
+static void checknext (LexState *ls, int c) {
+ check(ls, c);
+ luaX_next(ls);
+}
+
+
+#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
+
+
+
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (!testnext(ls, what)) {
+ if (where == ls->linenumber)
+ error_expected(ls, what);
+ else {
+ luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+ LUA_QS " expected (to close " LUA_QS " at line %d)",
+ luaX_token2str(ls, what), luaX_token2str(ls, who), where));
+ }
+ }
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check(ls, TK_NAME);
+ ts = ls->t.seminfo.ts;
+ luaX_next(ls);
+ return ts;
+}
+
+
+static void init_exp (expdesc *e, expkind k, int i) {
+ e->f = e->t = NO_JUMP;
+ e->k = k;
+ e->u.s.info = i;
+}
+
+
+static void codestring (LexState *ls, expdesc *e, TString *s) {
+ init_exp(e, VK, luaK_stringK(ls->fs, s));
+}
+
+
+static void checkname(LexState *ls, expdesc *e) {
+ codestring(ls, e, str_checkname(ls));
+}
+
+
+static int registerlocalvar (LexState *ls, TString *varname) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizelocvars;
+ luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
+ LocVar, SHRT_MAX, "too many local variables");
+ while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;
+ f->locvars[fs->nlocvars].varname = varname;
+ luaC_objbarrier(ls->L, f, varname);
+ return fs->nlocvars++;
+}
+
+
+#define new_localvarliteral(ls,v,n) \
+ new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n)
+
+
+static void new_localvar (LexState *ls, TString *name, int n) {
+ FuncState *fs = ls->fs;
+ luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables");
+ fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));
+}
+
+
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ fs->nactvar = cast_byte(fs->nactvar + nvars);
+ for (; nvars; nvars--) {
+ getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;
+ }
+}
+
+
+static void removevars (LexState *ls, int tolevel) {
+ FuncState *fs = ls->fs;
+ while (fs->nactvar > tolevel)
+ getlocvar(fs, --fs->nactvar).endpc = fs->pc;
+}
+
+
+static int indexupvalue (FuncState *fs, TString *name, expdesc *v) {
+ int i;
+ Proto *f = fs->f;
+ int oldsize = f->sizeupvalues;
+ for (i=0; i<f->nups; i++) {
+ if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {
+ lua_assert(f->upvalues[i] == name);
+ return i;
+ }
+ }
+ /* new one */
+ luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues");
+ luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,
+ TString *, MAX_INT, "");
+ while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;
+ f->upvalues[f->nups] = name;
+ luaC_objbarrier(fs->L, f, name);
+ lua_assert(v->k == VLOCAL || v->k == VUPVAL);
+ fs->upvalues[f->nups].k = cast_byte(v->k);
+ fs->upvalues[f->nups].info = cast_byte(v->u.s.info);
+ return f->nups++;
+}
+
+
+static int searchvar (FuncState *fs, TString *n) {
+ int i;
+ for (i=fs->nactvar-1; i >= 0; i--) {
+ if (n == getlocvar(fs, i).varname)
+ return i;
+ }
+ return -1; /* not found */
+}
+
+
+static void markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl && bl->nactvar > level) bl = bl->previous;
+ if (bl) bl->upval = 1;
+}
+
+
+static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+ if (fs == NULL) { /* no more levels? */
+ init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
+ return VGLOBAL;
+ }
+ else {
+ int v = searchvar(fs, n); /* look up at current level */
+ if (v >= 0) {
+ init_exp(var, VLOCAL, v);
+ if (!base)
+ markupval(fs, v); /* local will be used as an upval */
+ return VLOCAL;
+ }
+ else { /* not found at current level; try upper one */
+ if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)
+ return VGLOBAL;
+ var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
+ var->k = VUPVAL; /* upvalue in this level */
+ return VUPVAL;
+ }
+ }
+}
+
+
+static void singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ FuncState *fs = ls->fs;
+ if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
+ var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */
+}
+
+
+static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
+ FuncState *fs = ls->fs;
+ int extra = nvars - nexps;
+ if (hasmultret(e->k)) {
+ extra++; /* includes call itself */
+ if (extra < 0) extra = 0;
+ luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ if (extra > 1) luaK_reserveregs(fs, extra-1);
+ }
+ else {
+ if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */
+ if (extra > 0) {
+ int reg = fs->freereg;
+ luaK_reserveregs(fs, extra);
+ luaK_nil(fs, reg, extra);
+ }
+ }
+}
+
+
+static void enterlevel (LexState *ls) {
+ if (++ls->L->nCcalls > LUAI_MAXCCALLS)
+ luaX_lexerror(ls, "chunk has too many syntax levels", 0);
+}
+
+
+#define leavelevel(ls) ((ls)->L->nCcalls--)
+
+
+static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {
+ bl->breaklist = NO_JUMP;
+ bl->isbreakable = isbreakable;
+ bl->nactvar = fs->nactvar;
+ bl->upval = 0;
+ bl->previous = fs->bl;
+ fs->bl = bl;
+ lua_assert(fs->freereg == fs->nactvar);
+}
+
+
+static void leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ fs->bl = bl->previous;
+ removevars(fs->ls, bl->nactvar);
+ if (bl->upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ /* a block either controls scope or breaks (never both) */
+ lua_assert(!bl->isbreakable || !bl->upval);
+ lua_assert(bl->nactvar == fs->nactvar);
+ fs->freereg = fs->nactvar; /* free registers */
+ luaK_patchtohere(fs, bl->breaklist);
+}
+
+
+static void pushclosure (LexState *ls, FuncState *func, expdesc *v) {
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int oldsize = f->sizep;
+ int i;
+ luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
+ MAXARG_Bx, "constant table overflow");
+ while (oldsize < f->sizep) f->p[oldsize++] = NULL;
+ f->p[fs->np++] = func->f;
+ luaC_objbarrier(ls->L, f, func->f);
+ init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
+ for (i=0; i<func->f->nups; i++) {
+ OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
+ luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);
+ }
+}
+
+
+static void open_func (LexState *ls, FuncState *fs) {
+ lua_State *L = ls->L;
+ Proto *f = luaF_newproto(L);
+ fs->f = f;
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ fs->L = L;
+ ls->fs = fs;
+ fs->pc = 0;
+ fs->lasttarget = -1;
+ fs->jpc = NO_JUMP;
+ fs->freereg = 0;
+ fs->nk = 0;
+ fs->np = 0;
+ fs->nlocvars = 0;
+ fs->nactvar = 0;
+ fs->bl = NULL;
+ f->source = ls->source;
+ f->maxstacksize = 2; /* registers 0/1 are always valid */
+ fs->h = luaH_new(L, 0, 0);
+ /* anchor table of constants and prototype (to avoid being collected) */
+ sethvalue2s(L, L->top, fs->h);
+ incr_top(L);
+ setptvalue2s(L, L->top, f);
+ incr_top(L);
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ removevars(ls, 0);
+ luaK_ret(fs, 0, 0); /* final return */
+ luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ f->sizecode = fs->pc;
+ luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);
+ f->sizelineinfo = fs->pc;
+ luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);
+ f->sizek = fs->nk;
+ luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);
+ f->sizep = fs->np;
+ luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
+ f->sizelocvars = fs->nlocvars;
+ luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);
+ f->sizeupvalues = f->nups;
+ lua_assert(luaG_checkcode(f));
+ lua_assert(fs->bl == NULL);
+ ls->fs = fs->prev;
+ L->top -= 2; /* remove table and prototype from the stack */
+ /* last token read was anchored in defunct function; must reanchor it */
+ if (fs) anchor_token(ls);
+}
+
+
+Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
+ struct LexState lexstate;
+ struct FuncState funcstate;
+ lexstate.buff = buff;
+ luaX_setinput(L, &lexstate, z, luaS_new(L, name));
+ open_func(&lexstate, &funcstate);
+ funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
+ luaX_next(&lexstate); /* read first token */
+ chunk(&lexstate);
+ check(&lexstate, TK_EOS);
+ close_func(&lexstate);
+ lua_assert(funcstate.prev == NULL);
+ lua_assert(funcstate.f->nups == 0);
+ lua_assert(lexstate.fs == NULL);
+ return funcstate.f;
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+static void field (LexState *ls, expdesc *v) {
+ /* field -> ['.' | ':'] NAME */
+ FuncState *fs = ls->fs;
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ luaX_next(ls); /* skip the dot or colon */
+ checkname(ls, &key);
+ luaK_indexed(fs, v, &key);
+}
+
+
+static void yindex (LexState *ls, expdesc *v) {
+ /* index -> '[' expr ']' */
+ luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ luaK_exp2val(ls->fs, v);
+ checknext(ls, ']');
+}
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+struct ConsControl {
+ expdesc v; /* last list item read */
+ expdesc *t; /* table descriptor */
+ int nh; /* total number of `record' elements */
+ int na; /* total number of array elements */
+ int tostore; /* number of array elements pending to be stored */
+};
+
+
+static void recfield (LexState *ls, struct ConsControl *cc) {
+ /* recfield -> (NAME | `['exp1`]') = exp1 */
+ FuncState *fs = ls->fs;
+ int reg = ls->fs->freereg;
+ expdesc key, val;
+ int rkkey;
+ if (ls->t.token == TK_NAME) {
+ luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ checkname(ls, &key);
+ }
+ else /* ls->t.token == '[' */
+ yindex(ls, &key);
+ cc->nh++;
+ checknext(ls, '=');
+ rkkey = luaK_exp2RK(fs, &key);
+ expr(ls, &val);
+ luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));
+ fs->freereg = reg; /* free registers */
+}
+
+
+static void closelistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->v.k == VVOID) return; /* there is no list item */
+ luaK_exp2nextreg(fs, &cc->v);
+ cc->v.k = VVOID;
+ if (cc->tostore == LFIELDS_PER_FLUSH) {
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */
+ cc->tostore = 0; /* no more items pending */
+ }
+}
+
+
+static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
+ if (cc->tostore == 0) return;
+ if (hasmultret(cc->v.k)) {
+ luaK_setmultret(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);
+ cc->na--; /* do not count last expression (unknown number of elements) */
+ }
+ else {
+ if (cc->v.k != VVOID)
+ luaK_exp2nextreg(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);
+ }
+}
+
+
+static void listfield (LexState *ls, struct ConsControl *cc) {
+ expr(ls, &cc->v);
+ luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
+ cc->na++;
+ cc->tostore++;
+}
+
+
+static void constructor (LexState *ls, expdesc *t) {
+ /* constructor -> ?? */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+ struct ConsControl cc;
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, VRELOCABLE, pc);
+ init_exp(&cc.v, VVOID, 0); /* no value (yet) */
+ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */
+ checknext(ls, '{');
+ do {
+ lua_assert(cc.v.k == VVOID || cc.tostore > 0);
+ if (ls->t.token == '}') break;
+ closelistfield(fs, &cc);
+ switch(ls->t.token) {
+ case TK_NAME: { /* may be listfields or recfields */
+ luaX_lookahead(ls);
+ if (ls->lookahead.token != '=') /* expression? */
+ listfield(ls, &cc);
+ else
+ recfield(ls, &cc);
+ break;
+ }
+ case '[': { /* constructor_item -> recfield */
+ recfield(ls, &cc);
+ break;
+ }
+ default: { /* constructor_part -> listfield */
+ listfield(ls, &cc);
+ break;
+ }
+ }
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, &cc);
+ SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
+ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
+}
+
+/* }====================================================================== */
+
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ param { `,' param } ] */
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int nparams = 0;
+ f->is_vararg = 0;
+ if (ls->t.token != ')') { /* is `parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_NAME: { /* param -> NAME */
+ new_localvar(ls, str_checkname(ls), nparams++);
+ break;
+ }
+ case TK_DOTS: { /* param -> `...' */
+ luaX_next(ls);
+#if defined(LUA_COMPAT_VARARG)
+ /* use `arg' as default name */
+ new_localvarliteral(ls, "arg", nparams++);
+ f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;
+#endif
+ f->is_vararg |= VARARG_ISVARARG;
+ break;
+ }
+ default: luaX_syntaxerror(ls, "<name> or " LUA_QL("...") " expected");
+ }
+ } while (!f->is_vararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));
+ luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */
+}
+
+
+static void body (LexState *ls, expdesc *e, int needself, int line) {
+ /* body -> `(' parlist `)' chunk END */
+ FuncState new_fs;
+ open_func(ls, &new_fs);
+ new_fs.f->linedefined = line;
+ checknext(ls, '(');
+ if (needself) {
+ new_localvarliteral(ls, "self", 0);
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ chunk(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ close_func(ls);
+ pushclosure(ls, &new_fs, e);
+}
+
+
+static int explist1 (LexState *ls, expdesc *v) {
+ /* explist1 -> expr { `,' expr } */
+ int n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ luaK_exp2nextreg(ls->fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+}
+
+
+static void funcargs (LexState *ls, expdesc *f) {
+ FuncState *fs = ls->fs;
+ expdesc args;
+ int base, nparams;
+ int line = ls->linenumber;
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> `(' [ explist1 ] `)' */
+ if (line != ls->lastline)
+ luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)");
+ luaX_next(ls);
+ if (ls->t.token == ')') /* arg list is empty? */
+ args.k = VVOID;
+ else {
+ explist1(ls, &args);
+ luaK_setmultret(fs, &args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, &args);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ codestring(ls, &args, ls->t.seminfo.ts);
+ luaX_next(ls); /* must use `seminfo' before `next' */
+ break;
+ }
+ default: {
+ luaX_syntaxerror(ls, "function arguments expected");
+ return;
+ }
+ }
+ lua_assert(f->k == VNONRELOC);
+ base = f->u.s.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = LUA_MULTRET; /* open call */
+ else {
+ if (args.k != VVOID)
+ luaK_exp2nextreg(fs, &args); /* close last argument */
+ nparams = fs->freereg - (base+1);
+ }
+ init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+ luaK_fixline(fs, line);
+ fs->freereg = base+1; /* call remove function and arguments and leaves
+ (unless changed) one result */
+}
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void prefixexp (LexState *ls, expdesc *v) {
+ /* prefixexp -> NAME | '(' expr ')' */
+ switch (ls->t.token) {
+ case '(': {
+ int line = ls->linenumber;
+ luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ luaK_dischargevars(ls->fs, v);
+ return;
+ }
+ case TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ luaX_syntaxerror(ls, "unexpected symbol");
+ return;
+ }
+ }
+}
+
+
+static void primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp ->
+ prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */
+ FuncState *fs = ls->fs;
+ prefixexp(ls, v);
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* field */
+ field(ls, v);
+ break;
+ }
+ case '[': { /* `[' exp1 `]' */
+ expdesc key;
+ luaK_exp2anyreg(fs, v);
+ yindex(ls, &key);
+ luaK_indexed(fs, v, &key);
+ break;
+ }
+ case ':': { /* `:' NAME funcargs */
+ expdesc key;
+ luaX_next(ls);
+ checkname(ls, &key);
+ luaK_self(fs, v, &key);
+ funcargs(ls, v);
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* funcargs */
+ luaK_exp2nextreg(fs, v);
+ funcargs(ls, v);
+ break;
+ }
+ default: return;
+ }
+ }
+}
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
+ constructor | FUNCTION body | primaryexp */
+ switch (ls->t.token) {
+ case TK_NUMBER: {
+ init_exp(v, VKNUM, 0);
+ v->u.nval = ls->t.seminfo.r;
+ break;
+ }
+ case TK_STRING: {
+ codestring(ls, v, ls->t.seminfo.ts);
+ break;
+ }
+ case TK_NIL: {
+ init_exp(v, VNIL, 0);
+ break;
+ }
+ case TK_TRUE: {
+ init_exp(v, VTRUE, 0);
+ break;
+ }
+ case TK_FALSE: {
+ init_exp(v, VFALSE, 0);
+ break;
+ }
+ case TK_DOTS: { /* vararg */
+ FuncState *fs = ls->fs;
+ check_condition(ls, fs->f->is_vararg,
+ "cannot use " LUA_QL("...") " outside a vararg function");
+ fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case TK_FUNCTION: {
+ luaX_next(ls);
+ body(ls, v, 0, ls->linenumber);
+ return;
+ }
+ default: {
+ primaryexp(ls, v);
+ return;
+ }
+ }
+ luaX_next(ls);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ case '#': return OPR_LEN;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MUL;
+ case '/': return OPR_DIV;
+ case '%': return OPR_MOD;
+ case '^': return OPR_POW;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+static const struct {
+ lu_byte left; /* left priority for each binary operator */
+ lu_byte right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */
+ {10, 9}, {5, 4}, /* power and concat (right associative) */
+ {3, 3}, {3, 3}, /* equality and inequality */
+ {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */
+ {2, 2}, {1, 1} /* logical (and/or) */
+};
+
+#define UNARY_PRIORITY 8 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where `binop' is any binary operator with a priority higher than `limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) {
+ BinOpr op;
+ UnOpr uop;
+ enterlevel(ls);
+ uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) {
+ luaX_next(ls);
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls->fs, uop, v);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than `limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ luaX_next(ls);
+ luaK_infix(ls->fs, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls->fs, op, v, &v2);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static int block_follow (int token) {
+ switch (token) {
+ case TK_ELSE: case TK_ELSEIF: case TK_END:
+ case TK_UNTIL: case TK_EOS:
+ return 1;
+ default: return 0;
+ }
+}
+
+
+static void block (LexState *ls) {
+ /* block -> chunk */
+ FuncState *fs = ls->fs;
+ BlockCnt bl;
+ enterblock(fs, &bl, 0);
+ chunk(ls);
+ lua_assert(bl.breaklist == NO_JUMP);
+ leaveblock(fs);
+}
+
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+struct LHS_assign {
+ struct LHS_assign *prev;
+ expdesc v; /* variable (global, local, upvalue, or indexed) */
+};
+
+
+/*
+** check whether, in an assignment to a local variable, the local variable
+** is needed in a previous assignment (to a table). If so, save original
+** local value in a safe place and use this safe copy in the previous
+** assignment.
+*/
+static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int extra = fs->freereg; /* eventual position to save local variable */
+ int conflict = 0;
+ for (; lh; lh = lh->prev) {
+ if (lh->v.k == VINDEXED) {
+ if (lh->v.u.s.info == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.info = extra; /* previous assignment will use safe copy */
+ }
+ if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */
+ conflict = 1;
+ lh->v.u.s.aux = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ if (conflict) {
+ luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
+ expdesc e;
+ check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
+ "syntax error");
+ if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
+ struct LHS_assign nv;
+ nv.prev = lh;
+ primaryexp(ls, &nv.v);
+ if (nv.v.k == VLOCAL)
+ check_conflict(ls, lh, &nv.v);
+ luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
+ "variables in assignment");
+ assignment(ls, &nv, nvars+1);
+ }
+ else { /* assignment -> `=' explist1 */
+ int nexps;
+ checknext(ls, '=');
+ nexps = explist1(ls, &e);
+ if (nexps != nvars) {
+ adjust_assign(ls, nvars, nexps, &e);
+ if (nexps > nvars)
+ ls->fs->freereg -= nexps - nvars; /* remove extra values */
+ }
+ else {
+ luaK_setoneret(ls->fs, &e); /* close last expression */
+ luaK_storevar(ls->fs, &lh->v, &e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
+ luaK_storevar(ls->fs, &lh->v, &e);
+}
+
+
+static int cond (LexState *ls) {
+ /* cond -> exp */
+ expdesc v;
+ expr(ls, &v); /* read condition */
+ if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */
+ luaK_goiftrue(ls->fs, &v);
+ return v.f;
+}
+
+
+static void breakstat (LexState *ls) {
+ FuncState *fs = ls->fs;
+ BlockCnt *bl = fs->bl;
+ int upval = 0;
+ while (bl && !bl->isbreakable) {
+ upval |= bl->upval;
+ bl = bl->previous;
+ }
+ if (!bl)
+ luaX_syntaxerror(ls, "no loop to break");
+ if (upval)
+ luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+ luaK_concat(fs, &bl->breaklist, luaK_jump(fs));
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int whileinit;
+ int condexit;
+ BlockCnt bl;
+ luaX_next(ls); /* skip WHILE */
+ whileinit = luaK_getlabel(fs);
+ condexit = cond(ls);
+ enterblock(fs, &bl, 1);
+ checknext(ls, TK_DO);
+ block(ls);
+ luaK_patchlist(fs, luaK_jump(fs), whileinit);
+ check_match(ls, TK_END, TK_WHILE, line);
+ leaveblock(fs);
+ luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ int condexit;
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ BlockCnt bl1, bl2;
+ enterblock(fs, &bl1, 1); /* loop block */
+ enterblock(fs, &bl2, 0); /* scope block */
+ luaX_next(ls); /* skip REPEAT */
+ chunk(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ condexit = cond(ls); /* read condition (inside scope block) */
+ if (!bl2.upval) { /* no upvalues? */
+ leaveblock(fs); /* finish scope */
+ luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */
+ }
+ else { /* complete semantics when there are upvalues */
+ breakstat(ls); /* if condition then break */
+ luaK_patchtohere(ls->fs, condexit); /* else... */
+ leaveblock(fs); /* finish scope... */
+ luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */
+ }
+ leaveblock(fs); /* finish loop */
+}
+
+
+static int exp1 (LexState *ls) {
+ expdesc e;
+ int k;
+ expr(ls, &e);
+ k = e.k;
+ luaK_exp2nextreg(ls->fs, &e);
+ return k;
+}
+
+
+static void forbody (LexState *ls, int base, int line, int nvars, int isnum) {
+ /* forbody -> DO block */
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ int prep, endfor;
+ adjustlocalvars(ls, 3); /* control variables */
+ checknext(ls, TK_DO);
+ prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);
+ enterblock(fs, &bl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ luaK_patchtohere(fs, prep);
+ endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :
+ luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);
+ luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */
+ luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);
+}
+
+
+static void fornum (LexState *ls, TString *varname, int line) {
+ /* fornum -> NAME = exp1,exp1[,exp1] forbody */
+ FuncState *fs = ls->fs;
+ int base = fs->freereg;
+ new_localvarliteral(ls, "(for index)", 0);
+ new_localvarliteral(ls, "(for limit)", 1);
+ new_localvarliteral(ls, "(for step)", 2);
+ new_localvar(ls, varname, 3);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));
+ luaK_reserveregs(fs, 1);
+ }
+ forbody(ls, base, line, 1, 1);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME {,NAME} IN explist1 forbody */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nvars = 0;
+ int line;
+ int base = fs->freereg;
+ /* create control variables */
+ new_localvarliteral(ls, "(for generator)", nvars++);
+ new_localvarliteral(ls, "(for state)", nvars++);
+ new_localvarliteral(ls, "(for control)", nvars++);
+ /* create declared variables */
+ new_localvar(ls, indexname, nvars++);
+ while (testnext(ls, ','))
+ new_localvar(ls, str_checkname(ls), nvars++);
+ checknext(ls, TK_IN);
+ line = ls->linenumber;
+ adjust_assign(ls, 3, explist1(ls, &e), &e);
+ luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 3, 0);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ BlockCnt bl;
+ enterblock(fs, &bl, 1); /* scope for loop and control variables */
+ luaX_next(ls); /* skip `for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case TK_IN: forlist(ls, varname); break;
+ default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leaveblock(fs); /* loop scope (`break' jumps to this point) */
+}
+
+
+static int test_then_block (LexState *ls) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ int condexit;
+ luaX_next(ls); /* skip IF or ELSEIF */
+ condexit = cond(ls);
+ checknext(ls, TK_THEN);
+ block(ls); /* `then' part */
+ return condexit;
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ int flist;
+ int escapelist = NO_JUMP;
+ flist = test_then_block(ls); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ flist = test_then_block(ls); /* ELSEIF cond THEN block */
+ }
+ if (ls->t.token == TK_ELSE) {
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ luaK_patchtohere(fs, flist);
+ luaX_next(ls); /* skip ELSE (after patch, for correct line info) */
+ block(ls); /* `else' part */
+ }
+ else
+ luaK_concat(fs, &escapelist, flist);
+ luaK_patchtohere(fs, escapelist);
+ check_match(ls, TK_END, TK_IF, line);
+}
+
+
+static void localfunc (LexState *ls) {
+ expdesc v, b;
+ FuncState *fs = ls->fs;
+ new_localvar(ls, str_checkname(ls), 0);
+ init_exp(&v, VLOCAL, fs->freereg);
+ luaK_reserveregs(fs, 1);
+ adjustlocalvars(ls, 1);
+ body(ls, &b, 0, ls->linenumber);
+ luaK_storevar(fs, &v, &b);
+ /* debug information will only see the variable after this point! */
+ getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
+ int nvars = 0;
+ int nexps;
+ expdesc e;
+ do {
+ new_localvar(ls, str_checkname(ls), nvars++);
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist1(ls, &e);
+ else {
+ e.k = VVOID;
+ nexps = 0;
+ }
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME {field} [`:' NAME] */
+ int needself = 0;
+ singlevar(ls, v);
+ while (ls->t.token == '.')
+ field(ls, v);
+ if (ls->t.token == ':') {
+ needself = 1;
+ field(ls, v);
+ }
+ return needself;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int needself;
+ expdesc v, b;
+ luaX_next(ls); /* skip FUNCTION */
+ needself = funcname(ls, &v);
+ body(ls, &b, needself, line);
+ luaK_storevar(ls->fs, &v, &b);
+ luaK_fixline(ls->fs, line); /* definition `happens' in the first line */
+}
+
+
+static void exprstat (LexState *ls) {
+ /* stat -> func | assignment */
+ FuncState *fs = ls->fs;
+ struct LHS_assign v;
+ primaryexp(ls, &v.v);
+ if (v.v.k == VCALL) /* stat -> func */
+ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
+ else { /* stat -> assignment */
+ v.prev = NULL;
+ assignment(ls, &v, 1);
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN explist */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int first, nret; /* registers with returned values */
+ luaX_next(ls); /* skip RETURN */
+ if (block_follow(ls->t.token) || ls->t.token == ';')
+ first = nret = 0; /* return no values */
+ else {
+ nret = explist1(ls, &e); /* optional return values */
+ if (hasmultret(e.k)) {
+ luaK_setmultret(fs, &e);
+ if (e.k == VCALL && nret == 1) { /* tail call? */
+ SET_OPCODE(getcode(fs,&e), OP_TAILCALL);
+ lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);
+ }
+ first = fs->nactvar;
+ nret = LUA_MULTRET; /* return all values */
+ }
+ else {
+ if (nret == 1) /* only one single value? */
+ first = luaK_exp2anyreg(fs, &e);
+ else {
+ luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */
+ first = fs->nactvar; /* return all `active' values */
+ lua_assert(nret == fs->freereg - first);
+ }
+ }
+ }
+ luaK_ret(fs, first, nret);
+}
+
+
+static int statement (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ switch (ls->t.token) {
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ return 0;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ return 0;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ return 0;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ return 0;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ return 0;
+ }
+ case TK_FUNCTION: {
+ funcstat(ls, line); /* stat -> funcstat */
+ return 0;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ return 0;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ retstat(ls);
+ return 1; /* must be last statement */
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ luaX_next(ls); /* skip BREAK */
+ breakstat(ls);
+ return 1; /* must be last statement */
+ }
+ default: {
+ exprstat(ls);
+ return 0; /* to avoid warnings */
+ }
+ }
+}
+
+
+static void chunk (LexState *ls) {
+ /* chunk -> { stat [`;'] } */
+ int islast = 0;
+ enterlevel(ls);
+ while (!islast && !block_follow(ls->t.token)) {
+ islast = statement(ls);
+ testnext(ls, ';');
+ lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+ ls->fs->freereg >= ls->fs->nactvar);
+ ls->fs->freereg = ls->fs->nactvar; /* free registers */
+ }
+ leavelevel(ls);
+}
+
+/* }====================================================================== */
diff --git a/engines/sword25/util/lua/lparser.h b/engines/sword25/util/lua/lparser.h
new file mode 100644
index 0000000000..18836afd1c
--- /dev/null
+++ b/engines/sword25/util/lua/lparser.h
@@ -0,0 +1,82 @@
+/*
+** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression descriptor
+*/
+
+typedef enum {
+ VVOID, /* no value */
+ VNIL,
+ VTRUE,
+ VFALSE,
+ VK, /* info = index of constant in `k' */
+ VKNUM, /* nval = numerical value */
+ VLOCAL, /* info = local register */
+ VUPVAL, /* info = index of upvalue in `upvalues' */
+ VGLOBAL, /* info = index of table; aux = index of global name in `k' */
+ VINDEXED, /* info = table register; aux = index register (or `k') */
+ VJMP, /* info = instruction pc */
+ VRELOCABLE, /* info = instruction pc */
+ VNONRELOC, /* info = result register */
+ VCALL, /* info = instruction pc */
+ VVARARG /* info = instruction pc */
+} expkind;
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ struct { int info, aux; } s;
+ lua_Number nval;
+ } u;
+ int t; /* patch list of `exit when true' */
+ int f; /* patch list of `exit when false' */
+} expdesc;
+
+
+typedef struct upvaldesc {
+ lu_byte k;
+ lu_byte info;
+} upvaldesc;
+
+
+struct BlockCnt; /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ Table *h; /* table to find (and reuse) elements in `k' */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct lua_State *L; /* copy of the Lua state */
+ struct BlockCnt *bl; /* chain of current blocks */
+ int pc; /* next position to code (equivalent to `ncode') */
+ int lasttarget; /* `pc' of last `jump target' */
+ int jpc; /* list of pending jumps to `pc' */
+ int freereg; /* first free register */
+ int nk; /* number of elements in `k' */
+ int np; /* number of elements in `p' */
+ short nlocvars; /* number of elements in `locvars' */
+ lu_byte nactvar; /* number of active local variables */
+ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
+ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
+} FuncState;
+
+
+LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ const char *name);
+
+
+#endif
diff --git a/engines/sword25/util/lua/lstate.c b/engines/sword25/util/lua/lstate.c
new file mode 100644
index 0000000000..4313b83a0c
--- /dev/null
+++ b/engines/sword25/util/lua/lstate.c
@@ -0,0 +1,214 @@
+/*
+** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define lstate_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)
+#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
+#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
+
+
+/*
+** Main thread combines a thread state and the global state
+*/
+typedef struct LG {
+ lua_State l;
+ global_State g;
+} LG;
+
+
+
+static void stack_init (lua_State *L1, lua_State *L) {
+ /* initialize CallInfo array */
+ L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
+ L1->ci = L1->base_ci;
+ L1->size_ci = BASIC_CI_SIZE;
+ L1->end_ci = L1->base_ci + L1->size_ci - 1;
+ /* initialize stack array */
+ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
+ L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
+ L1->top = L1->stack;
+ L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
+ /* initialize first ci */
+ L1->ci->func = L1->top;
+ setnilvalue(L1->top++); /* `function' entry for this `ci' */
+ L1->base = L1->ci->base = L1->top;
+ L1->ci->top = L1->top + LUA_MINSTACK;
+}
+
+
+static void freestack (lua_State *L, lua_State *L1) {
+ luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
+ luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+}
+
+
+/*
+** open parts that may cause memory-allocation errors
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ global_State *g = G(L);
+ UNUSED(ud);
+ stack_init(L, L); /* init stack */
+ sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */
+ sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
+ luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */
+ luaT_init(L);
+ luaX_init(L);
+ luaS_fix(luaS_newliteral(L, MEMERRMSG));
+ g->GCthreshold = 4*g->totalbytes;
+}
+
+
+static void preinit_state (lua_State *L, global_State *g) {
+ G(L) = g;
+ L->stack = NULL;
+ L->stacksize = 0;
+ L->errorJmp = NULL;
+ L->hook = NULL;
+ L->hookmask = 0;
+ L->basehookcount = 0;
+ L->allowhook = 1;
+ resethookcount(L);
+ L->openupval = NULL;
+ L->size_ci = 0;
+ L->nCcalls = L->baseCcalls = 0;
+ L->status = 0;
+ L->base_ci = L->ci = NULL;
+ L->savedpc = NULL;
+ L->errfunc = 0;
+ setnilvalue(gt(L));
+}
+
+
+static void close_state (lua_State *L) {
+ global_State *g = G(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_freeall(L); /* collect all objects */
+ lua_assert(g->rootgc == obj2gco(L));
+ lua_assert(g->strt.nuse == 0);
+ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
+ luaZ_freebuffer(L, &g->buff);
+ freestack(L, L);
+ lua_assert(g->totalbytes == sizeof(LG));
+ (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);
+}
+
+
+lua_State *luaE_newthread (lua_State *L) {
+ lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
+ luaC_link(L, obj2gco(L1), LUA_TTHREAD);
+ preinit_state(L1, G(L));
+ stack_init(L1, L); /* init stack */
+ setobj2n(L, gt(L1), gt(L)); /* share table of globals */
+ L1->hookmask = L->hookmask;
+ L1->basehookcount = L->basehookcount;
+ L1->hook = L->hook;
+ resethookcount(L1);
+ lua_assert(iswhite(obj2gco(L1)));
+ return L1;
+}
+
+
+void luaE_freethread (lua_State *L, lua_State *L1) {
+ luaF_close(L1, L1->stack); /* close all upvalues for this thread */
+ lua_assert(L1->openupval == NULL);
+ luai_userstatefree(L1);
+ freestack(L, L1);
+ luaM_freemem(L, fromstate(L1), state_size(lua_State));
+}
+
+
+LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
+ int i;
+ lua_State *L;
+ global_State *g;
+ void *l = (*f)(ud, NULL, 0, state_size(LG));
+ if (l == NULL) return NULL;
+ L = tostate(l);
+ g = &((LG *)L)->g;
+ L->next = NULL;
+ L->tt = LUA_TTHREAD;
+ g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
+ L->marked = luaC_white(g);
+ set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
+ preinit_state(L, g);
+ g->frealloc = f;
+ g->ud = ud;
+ g->mainthread = L;
+ g->uvhead.u.l.prev = &g->uvhead;
+ g->uvhead.u.l.next = &g->uvhead;
+ g->GCthreshold = 0; /* mark it as unfinished state */
+ g->strt.size = 0;
+ g->strt.nuse = 0;
+ g->strt.hash = NULL;
+ setnilvalue(registry(L));
+ luaZ_initbuffer(L, &g->buff);
+ g->panic = NULL;
+ g->gcstate = GCSpause;
+ g->rootgc = obj2gco(L);
+ g->sweepstrgc = 0;
+ g->sweepgc = &g->rootgc;
+ g->gray = NULL;
+ g->grayagain = NULL;
+ g->weak = NULL;
+ g->tmudata = NULL;
+ g->totalbytes = sizeof(LG);
+ g->gcpause = LUAI_GCPAUSE;
+ g->gcstepmul = LUAI_GCMUL;
+ g->gcdept = 0;
+ for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
+ if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
+ /* memory allocation error: free partial state */
+ close_state(L);
+ L = NULL;
+ }
+ else
+ luai_userstateopen(L);
+ return L;
+}
+
+
+static void callallgcTM (lua_State *L, void *ud) {
+ UNUSED(ud);
+ luaC_callGCTM(L); /* call GC metamethods for all udata */
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ L = G(L)->mainthread; /* only the main thread can be closed */
+ lua_lock(L);
+ luaF_close(L, L->stack); /* close all upvalues for this thread */
+ luaC_separateudata(L, 1); /* separate udata that have GC metamethods */
+ L->errfunc = 0; /* no error function during GC metamethods */
+ do { /* repeat until no more errors */
+ L->ci = L->base_ci;
+ L->base = L->top = L->ci->base;
+ L->nCcalls = L->baseCcalls = 0;
+ } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);
+ lua_assert(G(L)->tmudata == NULL);
+ luai_userstateclose(L);
+ close_state(L);
+}
+
diff --git a/engines/sword25/util/lua/lstate.h b/engines/sword25/util/lua/lstate.h
new file mode 100644
index 0000000000..3bc575b6bc
--- /dev/null
+++ b/engines/sword25/util/lua/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/* table of globals */
+#define gt(L) (&L->l_gt)
+
+/* registry */
+#define registry(L) (&G(L)->l_registry)
+
+
+/* extra stack space to handle TM calls and some other extras */
+#define EXTRA_STACK 5
+
+
+#define BASIC_CI_SIZE 8
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+
+
+typedef struct stringtable {
+ GCObject **hash;
+ lu_int32 nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** informations about a call
+*/
+typedef struct CallInfo {
+ StkId base; /* base for this function */
+ StkId func; /* function index in the stack */
+ StkId top; /* top for this function */
+ const Instruction *savedpc;
+ int nresults; /* expected number of results from this function */
+ int tailcalls; /* number of tail calls lost under this entry */
+} CallInfo;
+
+
+
+#define curr_func(L) (clvalue(L->ci->func))
+#define ci_func(ci) (clvalue((ci)->func))
+#define f_isLua(ci) (!ci_func(ci)->c.isC)
+#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
+
+
+/*
+** `global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ stringtable strt; /* hash table for strings */
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to `frealloc' */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ int sweepstrgc; /* position of sweep in `strt' */
+ GCObject *rootgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* position of sweep in `rootgc' */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of weak tables (to be cleared) */
+ GCObject *tmudata; /* last element of list of userdata to be GC */
+ Mbuffer buff; /* temporary buffer for string concatentation */
+ lu_mem GCthreshold;
+ lu_mem totalbytes; /* number of bytes currently allocated */
+ lu_mem estimate; /* an estimate of number of bytes actually in use */
+ lu_mem gcdept; /* how much GC is `behind schedule' */
+ int gcpause; /* size of pause between successive GCs */
+ int gcstepmul; /* GC `granularity' */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ TValue l_registry;
+ struct lua_State *mainthread;
+ UpVal uvhead; /* head of double-linked list of all open upvalues */
+ struct Table *mt[NUM_TAGS]; /* metatables for basic types */
+ TString *tmname[TM_N]; /* array with tag-method names */
+} global_State;
+
+
+/*
+** `per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ StkId top; /* first free slot in the stack */
+ StkId base; /* base of current function */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ const Instruction *savedpc; /* `savedpc' of current function */
+ StkId stack_last; /* last free slot in the stack */
+ StkId stack; /* stack base */
+ CallInfo *end_ci; /* points after end of ci array*/
+ CallInfo *base_ci; /* array of CallInfo's */
+ int stacksize;
+ int size_ci; /* size of array `base_ci' */
+ unsigned short nCcalls; /* number of nested C calls */
+ unsigned short baseCcalls; /* nested C calls when resuming coroutine */
+ lu_byte hookmask;
+ lu_byte allowhook;
+ int basehookcount;
+ int hookcount;
+ lua_Hook hook;
+ TValue l_gt; /* table of globals */
+ TValue env; /* temporary place for environments */
+ GCObject *openupval; /* list of open upvalues in this stack */
+ GCObject *gclist;
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+};
+
+
+#define G(L) (L->l_G)
+
+
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+ GCheader gch;
+ union TString ts;
+ union Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct UpVal uv;
+ struct lua_State th; /* thread */
+};
+
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define gco2ts(o) (&rawgco2ts(o)->tsv)
+#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
+#define gco2u(o) (&rawgco2u(o)->uv)
+#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
+#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
+#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define ngcotouv(o) \
+ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
+
+/* macro to convert any Lua object into a GCObject */
+#define obj2gco(v) (cast(GCObject *, (v)))
+
+
+LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+
+#endif
+
diff --git a/engines/sword25/util/lua/lstring.c b/engines/sword25/util/lua/lstring.c
new file mode 100644
index 0000000000..49113151cc
--- /dev/null
+++ b/engines/sword25/util/lua/lstring.c
@@ -0,0 +1,111 @@
+/*
+** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lstring_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+
+void luaS_resize (lua_State *L, int newsize) {
+ GCObject **newhash;
+ stringtable *tb;
+ int i;
+ if (G(L)->gcstate == GCSsweepstring)
+ return; /* cannot resize during GC traverse */
+ newhash = luaM_newvector(L, newsize, GCObject *);
+ tb = &G(L)->strt;
+ for (i=0; i<newsize; i++) newhash[i] = NULL;
+ /* rehash */
+ for (i=0; i<tb->size; i++) {
+ GCObject *p = tb->hash[i];
+ while (p) { /* for each node in the list */
+ GCObject *next = p->gch.next; /* save next */
+ unsigned int h = gco2ts(p)->hash;
+ int h1 = lmod(h, newsize); /* new position */
+ lua_assert(cast_int(h%newsize) == lmod(h, newsize));
+ p->gch.next = newhash[h1]; /* chain it */
+ newhash[h1] = p;
+ p = next;
+ }
+ }
+ luaM_freearray(L, tb->hash, tb->size, TString *);
+ tb->size = newsize;
+ tb->hash = newhash;
+}
+
+
+static TString *newlstr (lua_State *L, const char *str, size_t l,
+ unsigned int h) {
+ TString *ts;
+ stringtable *tb;
+ if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
+ luaM_toobig(L);
+ ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
+ ts->tsv.len = l;
+ ts->tsv.hash = h;
+ ts->tsv.marked = luaC_white(G(L));
+ ts->tsv.tt = LUA_TSTRING;
+ ts->tsv.reserved = 0;
+ memcpy(ts+1, str, l*sizeof(char));
+ ((char *)(ts+1))[l] = '\0'; /* ending 0 */
+ tb = &G(L)->strt;
+ h = lmod(h, tb->size);
+ ts->tsv.next = tb->hash[h]; /* chain new entry */
+ tb->hash[h] = obj2gco(ts);
+ tb->nuse++;
+ if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
+ luaS_resize(L, tb->size*2); /* too crowded */
+ return ts;
+}
+
+
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ GCObject *o;
+ unsigned int h = cast(unsigned int, l); /* seed */
+ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */
+ size_t l1;
+ for (l1=l; l1>=step; l1-=step) /* compute hash */
+ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
+ for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
+ o != NULL;
+ o = o->gch.next) {
+ TString *ts = rawgco2ts(o);
+ if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
+ /* string may be dead */
+ if (isdead(G(L), o)) changewhite(o);
+ return ts;
+ }
+ }
+ return newlstr(L, str, l, h); /* not found */
+}
+
+
+Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {
+ Udata *u;
+ if (s > MAX_SIZET - sizeof(Udata))
+ luaM_toobig(L);
+ u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));
+ u->uv.marked = luaC_white(G(L)); /* is not finalized */
+ u->uv.tt = LUA_TUSERDATA;
+ u->uv.len = s;
+ u->uv.metatable = NULL;
+ u->uv.env = e;
+ /* chain it on udata list (after main thread) */
+ u->uv.next = G(L)->mainthread->next;
+ G(L)->mainthread->next = obj2gco(u);
+ return u;
+}
+
diff --git a/engines/sword25/util/lua/lstring.h b/engines/sword25/util/lua/lstring.h
new file mode 100644
index 0000000000..73a2ff8b38
--- /dev/null
+++ b/engines/sword25/util/lua/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
+
+#define sizeudata(u) (sizeof(union Udata)+(u)->len)
+
+#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
+
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+
+
+#endif
diff --git a/engines/sword25/util/lua/lstrlib.c b/engines/sword25/util/lua/lstrlib.c
new file mode 100644
index 0000000000..ca333ba168
--- /dev/null
+++ b/engines/sword25/util/lua/lstrlib.c
@@ -0,0 +1,868 @@
+/*
+** $Id: lstrlib.c,v 1.132.1.3 2007/12/28 15:32:23 roberto Exp $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/* macro to `unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, l);
+ return 1;
+}
+
+
+static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
+ /* relative string position: negative means back from end */
+ return (pos>=0) ? pos : (ptrdiff_t)len+pos+1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
+ ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
+ if (start < 1) start = 1;
+ if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
+ if (start <= end)
+ lua_pushlstring(L, s+start-1, end-start+1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int str_reverse (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ while (l--) luaL_addchar(&b, s[l]);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_addchar(&b, tolower(uchar(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ luaL_buffinit(L, &b);
+ for (i=0; i<l; i++)
+ luaL_addchar(&b, toupper(uchar(s[i])));
+ luaL_pushresult(&b);
+ return 1;
+}
+
+static int str_rep (lua_State *L) {
+ size_t l;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ int n = luaL_checkint(L, 2);
+ luaL_buffinit(L, &b);
+ while (n-- > 0)
+ luaL_addlstring(&b, s, l);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);
+ ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);
+ int n, i;
+ if (posi <= 0) posi = 1;
+ if ((size_t)pose > l) pose = l;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ n = (int)(pose - posi + 1);
+ if (posi + n <= pose) /* overflow? */
+ luaL_error(L, "string slice too long");
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i<n; i++)
+ lua_pushinteger(L, uchar(s[posi+i-1]));
+ return n;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i=1; i<=n; i++) {
+ int c = luaL_checkint(L, i);
+ luaL_argcheck(L, uchar(c) == c, i, "invalid value");
+ luaL_addchar(&b, uchar(c));
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int writer (lua_State *L, const void* b, size_t size, void* B) {
+ (void)L;
+ luaL_addlstring((luaL_Buffer*) B, (const char *)b, size);
+ return 0;
+}
+
+
+static int str_dump (lua_State *L) {
+ luaL_Buffer b;
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 1);
+ luaL_buffinit(L,&b);
+ if (lua_dump(L, writer, &b) != 0)
+ luaL_error(L, "unable to dump given function");
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+
+#define CAP_UNFINISHED (-1)
+#define CAP_POSITION (-2)
+
+typedef struct MatchState {
+ const char *src_init; /* init of source string */
+ const char *src_end; /* end (`\0') of source string */
+ lua_State *L;
+ int level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ ptrdiff_t len;
+ } capture[LUA_MAXCAPTURES];
+} MatchState;
+
+
+#define L_ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (MatchState *ms, int l) {
+ l -= '1';
+ if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
+ return luaL_error(ms->L, "invalid capture index");
+ return l;
+}
+
+
+static int capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a `]' */
+ if (*p == '\0')
+ luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")");
+ if (*(p++) == L_ESC && *p != '\0')
+ p++; /* skip escapes (e.g. `%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break;
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the `^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int singlematch (int c, const char *p, const char *ep) {
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+static const char *matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (*p == 0 || *(p+1) == 0)
+ luaL_error(ms->L, "unbalanced pattern");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p) {
+ init: /* using goto's to optimize tail recursion */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p+1) == ')') /* position capture? */
+ return start_capture(ms, s, p+2, CAP_POSITION);
+ else
+ return start_capture(ms, s, p+1, CAP_UNFINISHED);
+ }
+ case ')': { /* end capture */
+ return end_capture(ms, s, p+1);
+ }
+ case L_ESC: {
+ switch (*(p+1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p+2);
+ if (s == NULL) return NULL;
+ p+=4; goto init; /* else return match(ms, s, p+4); */
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (*p != '[')
+ luaL_error(ms->L, "missing " LUA_QL("[") " after "
+ LUA_QL("%%f") " in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s-1);
+ if (matchbracketclass(uchar(previous), p, ep-1) ||
+ !matchbracketclass(uchar(*s), p, ep-1)) return NULL;
+ p=ep; goto init; /* else return match(ms, s, ep); */
+ }
+ default: {
+ if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p+1)));
+ if (s == NULL) return NULL;
+ p+=2; goto init; /* else return match(ms, s, p+2) */
+ }
+ goto dflt; /* case default */
+ }
+ }
+ }
+ case '\0': { /* end of pattern */
+ return s; /* match succeeded */
+ }
+ case '$': {
+ if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
+ return (s == ms->src_end) ? s : NULL; /* check end of string */
+ else goto dflt;
+ }
+ default: dflt: { /* it is a pattern item */
+ const char *ep = classend(ms, p); /* points to what is next */
+ int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
+ switch (*ep) {
+ case '?': { /* optional */
+ const char *res;
+ if (m && ((res=match(ms, s+1, ep+1)) != NULL))
+ return res;
+ p=ep+1; goto init; /* else return match(ms, s, ep+1); */
+ }
+ case '*': { /* 0 or more repetitions */
+ return max_expand(ms, s, p, ep);
+ }
+ case '+': { /* 1 or more repetitions */
+ return (m ? max_expand(ms, s+1, p, ep) : NULL);
+ }
+ case '-': { /* 0 or more repetitions (minimum) */
+ return min_expand(ms, s, p, ep);
+ }
+ default: {
+ if (!m) return NULL;
+ s++; p=ep; goto init; /* else return match(ms, s+1, ep); */
+ }
+ }
+ }
+ }
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative `l1' */
+ else {
+ const char *init; /* to search for a `*s2' inside `s1' */
+ l2--; /* 1st char will be checked by `memchr' */
+ l1 = l1-l2; /* `s2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct `l1' and `s1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+static void push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ if (i >= ms->level) {
+ if (i == 0) /* ms->level == 0, too */
+ lua_pushlstring(ms->L, s, e - s); /* add whole match */
+ else
+ luaL_error(ms->L, "invalid capture index");
+ }
+ else {
+ ptrdiff_t l = ms->capture[i].len;
+ if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture");
+ if (l == CAP_POSITION)
+ lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
+ else
+ lua_pushlstring(ms->L, ms->capture[i].init, l);
+ }
+}
+
+
+static int push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+static int str_find_aux (lua_State *L, int find) {
+ size_t l1, l2;
+ const char *s = luaL_checklstring(L, 1, &l1);
+ const char *p = luaL_checklstring(L, 2, &l2);
+ ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
+ if (init < 0) init = 0;
+ else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;
+ if (find && (lua_toboolean(L, 4) || /* explicit request? */
+ strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
+ /* do a plain search */
+ const char *s2 = lmemfind(s+init, l1-init, p, l2);
+ if (s2) {
+ lua_pushinteger(L, s2-s+1);
+ lua_pushinteger(L, s2-s+l2);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ const char *s1=s+init;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+l1;
+ do {
+ const char *res;
+ ms.level = 0;
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, s1-s+1); /* start */
+ lua_pushinteger(L, res-s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ lua_pushnil(L); /* not found */
+ return 1;
+}
+
+
+static int str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+static int gmatch_aux (lua_State *L) {
+ MatchState ms;
+ size_t ls;
+ const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
+ const char *p = lua_tostring(L, lua_upvalueindex(2));
+ const char *src;
+ ms.L = L;
+ ms.src_init = s;
+ ms.src_end = s+ls;
+ for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));
+ src <= ms.src_end;
+ src++) {
+ const char *e;
+ ms.level = 0;
+ if ((e = match(&ms, src, p)) != NULL) {
+ lua_Integer newstart = e-s;
+ if (e == src) newstart++; /* empty match? go at least one position */
+ lua_pushinteger(L, newstart);
+ lua_replace(L, lua_upvalueindex(3));
+ return push_captures(&ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int gmatch (lua_State *L) {
+ luaL_checkstring(L, 1);
+ luaL_checkstring(L, 2);
+ lua_settop(L, 2);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+
+static int gfind_nodef (lua_State *L) {
+ return luaL_error(L, LUA_QL("string.gfind") " was renamed to "
+ LUA_QL("string.gmatch"));
+}
+
+
+static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l, i;
+ const char *news = lua_tolstring(ms->L, 3, &l);
+ for (i = 0; i < l; i++) {
+ if (news[i] != L_ESC)
+ luaL_addchar(b, news[i]);
+ else {
+ i++; /* skip ESC */
+ if (!isdigit(uchar(news[i])))
+ luaL_addchar(b, news[i]);
+ else if (news[i] == '0')
+ luaL_addlstring(b, s, e - s);
+ else {
+ push_onecapture(ms, news[i] - '1', s, e);
+ luaL_addvalue(b); /* add capture to accumulated result */
+ }
+ }
+ }
+}
+
+
+static void add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ lua_State *L = ms->L;
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING: {
+ add_s(ms, b, s, e);
+ return;
+ }
+ case LUA_TFUNCTION: {
+ int n;
+ lua_pushvalue(L, 3);
+ n = push_captures(ms, s, e);
+ lua_call(L, n, 1);
+ break;
+ }
+ case LUA_TTABLE: {
+ push_onecapture(ms, 0, s, e);
+ lua_gettable(L, 3);
+ break;
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1);
+ lua_pushlstring(L, s, e - s); /* keep original text */
+ }
+ else if (!lua_isstring(L, -1))
+ luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
+ luaL_addvalue(b); /* add result to accumulator */
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl;
+ const char *src = luaL_checklstring(L, 1, &srcl);
+ const char *p = luaL_checkstring(L, 2);
+ int tr = lua_type(L, 3);
+ int max_s = luaL_optint(L, 4, srcl+1);
+ int anchor = (*p == '^') ? (p++, 1) : 0;
+ int n = 0;
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
+ "string/function/table expected");
+ luaL_buffinit(L, &b);
+ ms.L = L;
+ ms.src_init = src;
+ ms.src_end = src+srcl;
+ while (n < max_s) {
+ const char *e;
+ ms.level = 0;
+ e = match(&ms, src, p);
+ if (e) {
+ n++;
+ add_value(&ms, &b, src, e);
+ }
+ if (e && e>src) /* non empty match? */
+ src = e; /* skip it */
+ else if (src < ms.src_end)
+ luaL_addchar(&b, *src++);
+ else break;
+ if (anchor) break;
+ }
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b);
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
+#define MAX_ITEM 512
+/* valid flags in a format specification */
+#define FLAGS "-+ #0"
+/*
+** maximum size of each format specification (such as '%-099.99d')
+** (+10 accounts for %99.99x plus margin of error)
+*/
+#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
+
+
+static void addquoted (lua_State *L, luaL_Buffer *b, int arg) {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ luaL_addchar(b, '"');
+ while (l--) {
+ switch (*s) {
+ case '"': case '\\': case '\n': {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ break;
+ }
+ case '\r': {
+ luaL_addlstring(b, "\\r", 2);
+ break;
+ }
+ case '\0': {
+ luaL_addlstring(b, "\\000", 4);
+ break;
+ }
+ default: {
+ luaL_addchar(b, *s);
+ break;
+ }
+ }
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
+ const char *p = strfrmt;
+ while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
+ if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
+ luaL_error(L, "invalid format (repeated flags)");
+ if (isdigit(uchar(*p))) p++; /* skip width */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ if (*p == '.') {
+ p++;
+ if (isdigit(uchar(*p))) p++; /* skip precision */
+ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
+ }
+ if (isdigit(uchar(*p)))
+ luaL_error(L, "invalid format (width or precision too long)");
+ *(form++) = '%';
+ strncpy(form, strfrmt, p - strfrmt + 1);
+ form += p - strfrmt + 1;
+ *form = '\0';
+ return p;
+}
+
+
+static void addintlen (char *form) {
+ size_t l = strlen(form);
+ char spec = form[l - 1];
+ strcpy(form + l - 1, LUA_INTFRMLEN);
+ form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
+ form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
+}
+
+
+static int str_format (lua_State *L) {
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format (`%...') */
+ char buff[MAX_ITEM]; /* to store the formatted item */
+ arg++;
+ strfrmt = scanformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ sprintf(buff, form, (int)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'd': case 'i': {
+ addintlen(form);
+ sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'o': case 'u': case 'x': case 'X': {
+ addintlen(form);
+ sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'e': case 'E': case 'f':
+ case 'g': case 'G': {
+ sprintf(buff, form, (double)luaL_checknumber(L, arg));
+ break;
+ }
+ case 'q': {
+ addquoted(L, &b, arg);
+ continue; /* skip the 'addsize' at the end */
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ if (!strchr(form, '.') && l >= 100) {
+ /* no precision and string is too long to be formatted;
+ keep original string */
+ lua_pushvalue(L, arg);
+ luaL_addvalue(&b);
+ continue; /* skip the `addsize' at the end */
+ }
+ else {
+ sprintf(buff, form, s);
+ break;
+ }
+ }
+ default: { /* also treat cases `pnLlh' */
+ return luaL_error(L, "invalid option " LUA_QL("%%%c") " to "
+ LUA_QL("format"), *(strfrmt - 1));
+ }
+ }
+ luaL_addlstring(&b, buff, strlen(buff));
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static const luaL_Reg strlib[] = {
+ {"byte", str_byte},
+ {"char", str_char},
+ {"dump", str_dump},
+ {"find", str_find},
+ {"format", str_format},
+ {"gfind", gfind_nodef},
+ {"gmatch", gmatch},
+ {"gsub", str_gsub},
+ {"len", str_len},
+ {"lower", str_lower},
+ {"match", str_match},
+ {"rep", str_rep},
+ {"reverse", str_reverse},
+ {"sub", str_sub},
+ {"upper", str_upper},
+ {NULL, NULL}
+};
+
+
+static void createmetatable (lua_State *L) {
+ lua_createtable(L, 0, 1); /* create metatable for strings */
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2);
+ lua_setmetatable(L, -2); /* set string metatable */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* string library... */
+ lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** Open string library
+*/
+LUALIB_API int luaopen_string (lua_State *L) {
+ luaL_register(L, LUA_STRLIBNAME, strlib);
+#if defined(LUA_COMPAT_GFIND)
+ lua_getfield(L, -1, "gmatch");
+ lua_setfield(L, -2, "gfind");
+#endif
+ createmetatable(L);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/ltable.c b/engines/sword25/util/lua/ltable.c
new file mode 100644
index 0000000000..ec84f4fabc
--- /dev/null
+++ b/engines/sword25/util/lua/ltable.c
@@ -0,0 +1,588 @@
+/*
+** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables).
+** Tables keep its elements in two parts: an array part and a hash part.
+** Non-negative integer keys are all candidates to be kept in the array
+** part. The actual size of the array is the largest `n' such that at
+** least half the slots between 0 and n are in use.
+** Hash uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the `original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** Hence even when the load factor reaches 100%, performance remains good.
+*/
+
+#include <math.h>
+#include <string.h>
+
+#define ltable_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "ltable.h"
+
+
+/*
+** max size of array part is 2^MAXBITS
+*/
+#if LUAI_BITSINT > 26
+#define MAXBITS 26
+#else
+#define MAXBITS (LUAI_BITSINT-2)
+#endif
+
+#define MAXASIZE (1 << MAXBITS)
+
+
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+
+#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)
+#define hashboolean(t,p) hashpow2(t, p)
+
+
+/*
+** for some types, it is better to avoid modulus by power of 2, as
+** they tend to have many 2 factors.
+*/
+#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+
+#define hashpointer(t,p) hashmod(t, IntPoint(p))
+
+
+/*
+** number of ints inside a lua_Number
+*/
+#define numints cast_int(sizeof(lua_Number)/sizeof(int))
+
+
+
+#define dummynode (&dummynode_)
+
+static const Node dummynode_ = {
+ {{NULL}, LUA_TNIL}, /* value */
+ {{{NULL}, LUA_TNIL, NULL}} /* key */
+};
+
+
+/*
+** hash for lua_Numbers
+*/
+static Node *hashnum (const Table *t, lua_Number n) {
+ unsigned int a[numints];
+ int i;
+ if (luai_numeq(n, 0)) /* avoid problems with -0 */
+ return gnode(t, 0);
+ memcpy(a, &n, sizeof(a));
+ for (i = 1; i < numints; i++) a[0] += a[i];
+ return hashmod(t, a[0]);
+}
+
+
+
+/*
+** returns the `main' position of an element in a table (that is, the index
+** of its hash value)
+*/
+static Node *mainposition (const Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNUMBER:
+ return hashnum(t, nvalue(key));
+ case LUA_TSTRING:
+ return hashstr(t, rawtsvalue(key));
+ case LUA_TBOOLEAN:
+ return hashboolean(t, bvalue(key));
+ case LUA_TLIGHTUSERDATA:
+ return hashpointer(t, pvalue(key));
+ default:
+ return hashpointer(t, gcvalue(key));
+ }
+}
+
+
+/*
+** returns the index for `key' if `key' is an appropriate key to live in
+** the array part of the table, -1 otherwise.
+*/
+static int arrayindex (const TValue *key) {
+ if (ttisnumber(key)) {
+ lua_Number n = nvalue(key);
+ int k;
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), n))
+ return k;
+ }
+ return -1; /* `key' did not match some condition */
+}
+
+
+/*
+** returns the index of a `key' for table traversals. First goes all
+** elements in the array part, then elements in the hash part. The
+** beginning of a traversal is signalled by -1.
+*/
+static int findindex (lua_State *L, Table *t, StkId key) {
+ int i;
+ if (ttisnil(key)) return -1; /* first iteration */
+ i = arrayindex(key);
+ if (0 < i && i <= t->sizearray) /* is `key' inside array part? */
+ return i-1; /* yes; that's the index (corrected to C) */
+ else {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ /* key may be dead already, but it is ok to use it in `next' */
+ if (luaO_rawequalObj(key2tval(n), key) ||
+ (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) &&
+ gcvalue(gkey(n)) == gcvalue(key))) {
+ i = cast_int(n - gnode(t, 0)); /* key index in hash table */
+ /* hash elements are numbered after array ones */
+ return i + t->sizearray;
+ }
+ else n = gnext(n);
+ } while (n);
+ luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */
+ return 0; /* to avoid warnings */
+ }
+}
+
+
+int luaH_next (lua_State *L, Table *t, StkId key) {
+ int i = findindex(L, t, key); /* find original element */
+ for (i++; i < t->sizearray; i++) { /* try first array part */
+ if (!ttisnil(&t->array[i])) { /* a non-nil value? */
+ setnvalue(key, cast_num(i+1));
+ setobj2s(L, key+1, &t->array[i]);
+ return 1;
+ }
+ }
+ for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */
+ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
+ setobj2s(L, key, key2tval(gnode(t, i)));
+ setobj2s(L, key+1, gval(gnode(t, i)));
+ return 1;
+ }
+ }
+ return 0; /* no more elements */
+}
+
+
+/*
+** {=============================================================
+** Rehash
+** ==============================================================
+*/
+
+
+static int computesizes (int nums[], int *narray) {
+ int i;
+ int twotoi; /* 2^i */
+ int a = 0; /* number of elements smaller than 2^i */
+ int na = 0; /* number of elements to go to array part */
+ int n = 0; /* optimal size for array part */
+ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
+ if (nums[i] > 0) {
+ a += nums[i];
+ if (a > twotoi/2) { /* more than half elements present? */
+ n = twotoi; /* optimal size (till now) */
+ na = a; /* all elements smaller than n will go to array part */
+ }
+ }
+ if (a == *narray) break; /* all elements already counted */
+ }
+ *narray = n;
+ lua_assert(*narray/2 <= na && na <= *narray);
+ return na;
+}
+
+
+static int countint (const TValue *key, int *nums) {
+ int k = arrayindex(key);
+ if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */
+ nums[ceillog2(k)]++; /* count as such */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+static int numusearray (const Table *t, int *nums) {
+ int lg;
+ int ttlg; /* 2^lg */
+ int ause = 0; /* summation of `nums' */
+ int i = 1; /* count to traverse all array keys */
+ for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */
+ int lc = 0; /* counter */
+ int lim = ttlg;
+ if (lim > t->sizearray) {
+ lim = t->sizearray; /* adjust upper limit */
+ if (i > lim)
+ break; /* no more elements to count */
+ }
+ /* count elements in range (2^(lg-1), 2^lg] */
+ for (; i <= lim; i++) {
+ if (!ttisnil(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+
+static int numusehash (const Table *t, int *nums, int *pnasize) {
+ int totaluse = 0; /* total number of elements */
+ int ause = 0; /* summation of `nums' */
+ int i = sizenode(t);
+ while (i--) {
+ Node *n = &t->node[i];
+ if (!ttisnil(gval(n))) {
+ ause += countint(key2tval(n), nums);
+ totaluse++;
+ }
+ }
+ *pnasize += ause;
+ return totaluse;
+}
+
+
+static void setarrayvector (lua_State *L, Table *t, int size) {
+ int i;
+ luaM_reallocvector(L, t->array, t->sizearray, size, TValue);
+ for (i=t->sizearray; i<size; i++)
+ setnilvalue(&t->array[i]);
+ t->sizearray = size;
+}
+
+
+static void setnodevector (lua_State *L, Table *t, int size) {
+ int lsize;
+ if (size == 0) { /* no elements to hash part? */
+ t->node = cast(Node *, dummynode); /* use common `dummynode' */
+ lsize = 0;
+ }
+ else {
+ int i;
+ lsize = ceillog2(size);
+ if (lsize > MAXBITS)
+ luaG_runerror(L, "table overflow");
+ size = twoto(lsize);
+ t->node = luaM_newvector(L, size, Node);
+ for (i=0; i<size; i++) {
+ Node *n = gnode(t, i);
+ gnext(n) = NULL;
+ setnilvalue(gkey(n));
+ setnilvalue(gval(n));
+ }
+ }
+ t->lsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, size); /* all positions are free */
+}
+
+
+static void resize (lua_State *L, Table *t, int nasize, int nhsize) {
+ int i;
+ int oldasize = t->sizearray;
+ int oldhsize = t->lsizenode;
+ Node *nold = t->node; /* save old hash ... */
+ if (nasize > oldasize) /* array part must grow? */
+ setarrayvector(L, t, nasize);
+ /* create new hash part with appropriate size */
+ setnodevector(L, t, nhsize);
+ if (nasize < oldasize) { /* array part must shrink? */
+ t->sizearray = nasize;
+ /* re-insert elements from vanishing slice */
+ for (i=nasize; i<oldasize; i++) {
+ if (!ttisnil(&t->array[i]))
+ setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]);
+ }
+ /* shrink array */
+ luaM_reallocvector(L, t->array, oldasize, nasize, TValue);
+ }
+ /* re-insert elements from hash part */
+ for (i = twoto(oldhsize) - 1; i >= 0; i--) {
+ Node *old = nold+i;
+ if (!ttisnil(gval(old)))
+ setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old));
+ }
+ if (nold != dummynode)
+ luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */
+}
+
+
+void luaH_resizearray (lua_State *L, Table *t, int nasize) {
+ int nsize = (t->node == dummynode) ? 0 : sizenode(t);
+ resize(L, t, nasize, nsize);
+}
+
+
+static void rehash (lua_State *L, Table *t, const TValue *ek) {
+ int nasize, na;
+ int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */
+ int i;
+ int totaluse;
+ for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */
+ nasize = numusearray(t, nums); /* count keys in array part */
+ totaluse = nasize; /* all those keys are integer keys */
+ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */
+ /* count extra key */
+ nasize += countint(ek, nums);
+ totaluse++;
+ /* compute new size for array part */
+ na = computesizes(nums, &nasize);
+ /* resize the table to new computed sizes */
+ resize(L, t, nasize, totaluse - na);
+}
+
+
+
+/*
+** }=============================================================
+*/
+
+
+Table *luaH_new (lua_State *L, int narray, int nhash) {
+ Table *t = luaM_new(L, Table);
+ luaC_link(L, obj2gco(t), LUA_TTABLE);
+ t->metatable = NULL;
+ t->flags = cast_byte(~0);
+ /* temporary values (kept only if some malloc fails) */
+ t->array = NULL;
+ t->sizearray = 0;
+ t->lsizenode = 0;
+ t->node = cast(Node *, dummynode);
+ setarrayvector(L, t, narray);
+ setnodevector(L, t, nhash);
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Table *t) {
+ if (t->node != dummynode)
+ luaM_freearray(L, t->node, sizenode(t), Node);
+ luaM_freearray(L, t->array, t->sizearray, TValue);
+ luaM_free(L, t);
+}
+
+
+static Node *getfreepos (Table *t) {
+ while (t->lastfree-- > t->node) {
+ if (ttisnil(gkey(t->lastfree)))
+ return t->lastfree;
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+
+/*
+** inserts a new key into a hash table; first, check whether key's main
+** position is free. If not, check whether colliding node is in its main
+** position or not: if it is not, move colliding node to an empty place and
+** put new key in its main position; otherwise (colliding node is in its main
+** position), new key goes to an empty position.
+*/
+static TValue *newkey (lua_State *L, Table *t, const TValue *key) {
+ Node *mp = mainposition(t, key);
+ if (!ttisnil(gval(mp)) || mp == dummynode) {
+ Node *othern;
+ Node *n = getfreepos(t); /* get a free place */
+ if (n == NULL) { /* cannot find a free place? */
+ rehash(L, t, key); /* grow table */
+ return luaH_set(L, t, key); /* re-insert key into grown table */
+ }
+ lua_assert(n != dummynode);
+ othern = mainposition(t, key2tval(mp));
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (gnext(othern) != mp) othern = gnext(othern); /* find previous */
+ gnext(othern) = n; /* redo the chain with `n' in place of `mp' */
+ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ gnext(mp) = NULL; /* now `mp' is free */
+ setnilvalue(gval(mp));
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ gnext(n) = gnext(mp); /* chain new position */
+ gnext(mp) = n;
+ mp = n;
+ }
+ }
+ gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;
+ luaC_barriert(L, t, key);
+ lua_assert(ttisnil(gval(mp)));
+ return gval(mp);
+}
+
+
+/*
+** search function for integers
+*/
+const TValue *luaH_getnum (Table *t, int key) {
+ /* (1 <= key && key <= t->sizearray) */
+ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
+ return &t->array[key-1];
+ else {
+ lua_Number nk = cast_num(key);
+ Node *n = hashnum(t, nk);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+}
+
+
+/*
+** search function for strings
+*/
+const TValue *luaH_getstr (Table *t, TString *key) {
+ Node *n = hashstr(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+}
+
+
+/*
+** main search function
+*/
+const TValue *luaH_get (Table *t, const TValue *key) {
+ switch (ttype(key)) {
+ case LUA_TNIL: return luaO_nilobject;
+ case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));
+ case LUA_TNUMBER: {
+ int k;
+ lua_Number n = nvalue(key);
+ lua_number2int(k, n);
+ if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
+ return luaH_getnum(t, k); /* use specialized version */
+ /* else go through */
+ }
+ default: {
+ Node *n = mainposition(t, key);
+ do { /* check whether `key' is somewhere in the chain */
+ if (luaO_rawequalObj(key2tval(n), key))
+ return gval(n); /* that's it */
+ else n = gnext(n);
+ } while (n);
+ return luaO_nilobject;
+ }
+ }
+}
+
+
+TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
+ const TValue *p = luaH_get(t, key);
+ t->flags = 0;
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ if (ttisnil(key)) luaG_runerror(L, "table index is nil");
+ else if (ttisnumber(key) && luai_numisnan(nvalue(key)))
+ luaG_runerror(L, "table index is NaN");
+ return newkey(L, t, key);
+ }
+}
+
+
+TValue *luaH_setnum (lua_State *L, Table *t, int key) {
+ const TValue *p = luaH_getnum(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setnvalue(&k, cast_num(key));
+ return newkey(L, t, &k);
+ }
+}
+
+
+TValue *luaH_setstr (lua_State *L, Table *t, TString *key) {
+ const TValue *p = luaH_getstr(t, key);
+ if (p != luaO_nilobject)
+ return cast(TValue *, p);
+ else {
+ TValue k;
+ setsvalue(L, &k, key);
+ return newkey(L, t, &k);
+ }
+}
+
+
+static int unbound_search (Table *t, unsigned int j) {
+ unsigned int i = j; /* i is zero or a present index */
+ j++;
+ /* find `i' and `j' such that i is present and j is not */
+ while (!ttisnil(luaH_getnum(t, j))) {
+ i = j;
+ j *= 2;
+ if (j > cast(unsigned int, MAX_INT)) { /* overflow? */
+ /* table was built with bad purposes: resort to linear search */
+ i = 1;
+ while (!ttisnil(luaH_getnum(t, i))) i++;
+ return i - 1;
+ }
+ }
+ /* now do a binary search between them */
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(luaH_getnum(t, m))) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+/*
+** Try to find a boundary in table `t'. A `boundary' is an integer index
+** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
+*/
+int luaH_getn (Table *t) {
+ unsigned int j = t->sizearray;
+ if (j > 0 && ttisnil(&t->array[j - 1])) {
+ /* there is a boundary in the array part: (binary) search for it */
+ unsigned int i = 0;
+ while (j - i > 1) {
+ unsigned int m = (i+j)/2;
+ if (ttisnil(&t->array[m - 1])) j = m;
+ else i = m;
+ }
+ return i;
+ }
+ /* else must find a boundary in hash part */
+ else if (t->node == dummynode) /* hash part is empty? */
+ return j; /* that is easy... */
+ else return unbound_search(t, j);
+}
+
+
+
+#if defined(LUA_DEBUG)
+
+Node *luaH_mainposition (const Table *t, const TValue *key) {
+ return mainposition(t, key);
+}
+
+int luaH_isdummy (Node *n) { return n == dummynode; }
+
+#endif
diff --git a/engines/sword25/util/lua/ltable.h b/engines/sword25/util/lua/ltable.h
new file mode 100644
index 0000000000..f5b9d5ead0
--- /dev/null
+++ b/engines/sword25/util/lua/ltable.h
@@ -0,0 +1,40 @@
+/*
+** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define gnode(t,i) (&(t)->node[i])
+#define gkey(n) (&(n)->i_key.nk)
+#define gval(n) (&(n)->i_val)
+#define gnext(n) ((n)->i_key.nk.next)
+
+#define key2tval(n) (&(n)->i_key.tvk)
+
+
+LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
+LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);
+LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
+LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
+LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);
+LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);
+LUAI_FUNC void luaH_free (lua_State *L, Table *t);
+LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
+LUAI_FUNC int luaH_getn (Table *t);
+
+
+#if defined(LUA_DEBUG)
+LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
+LUAI_FUNC int luaH_isdummy (Node *n);
+#endif
+
+
+#endif
diff --git a/engines/sword25/util/lua/ltablib.c b/engines/sword25/util/lua/ltablib.c
new file mode 100644
index 0000000000..06f1c37be1
--- /dev/null
+++ b/engines/sword25/util/lua/ltablib.c
@@ -0,0 +1,279 @@
+/*
+** $Id: ltablib.c,v 1.38.1.2 2007/12/28 15:32:23 roberto Exp $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stddef.h>
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))
+
+
+static int foreachi (lua_State *L) {
+ int i;
+ int n = aux_getn(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ for (i=1; i <= n; i++) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushinteger(L, i); /* 1st argument */
+ lua_rawgeti(L, 1, i); /* 2nd argument */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 1); /* remove nil result */
+ }
+ return 0;
+}
+
+
+static int foreach (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pushvalue(L, 2); /* function */
+ lua_pushvalue(L, -3); /* key */
+ lua_pushvalue(L, -3); /* value */
+ lua_call(L, 2, 1);
+ if (!lua_isnil(L, -1))
+ return 1;
+ lua_pop(L, 2); /* remove value and result */
+ }
+ return 0;
+}
+
+
+static int maxn (lua_State *L) {
+ lua_Number max = 0;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, 1)) {
+ lua_pop(L, 1); /* remove value */
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ lua_Number v = lua_tonumber(L, -1);
+ if (v > max) max = v;
+ }
+ }
+ lua_pushnumber(L, max);
+ return 1;
+}
+
+
+static int getn (lua_State *L) {
+ lua_pushinteger(L, aux_getn(L, 1));
+ return 1;
+}
+
+
+static int setn (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+#ifndef luaL_setn
+ luaL_setn(L, 1, luaL_checkint(L, 2));
+#else
+ luaL_error(L, LUA_QL("setn") " is obsolete");
+#endif
+ lua_pushvalue(L, 1);
+ return 1;
+}
+
+
+static int tinsert (lua_State *L) {
+ int e = aux_getn(L, 1) + 1; /* first empty element */
+ int pos; /* where to insert new element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ int i;
+ pos = luaL_checkint(L, 2); /* 2nd argument is the position */
+ if (pos > e) e = pos; /* `grow' array if necessary */
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_rawgeti(L, 1, i-1);
+ lua_rawseti(L, 1, i); /* t[i] = t[i-1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
+ }
+ }
+ luaL_setn(L, 1, e); /* new size */
+ lua_rawseti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int tremove (lua_State *L) {
+ int e = aux_getn(L, 1);
+ int pos = luaL_optint(L, 2, e);
+ if (!(1 <= pos && pos <= e)) /* position is outside bounds? */
+ return 0; /* nothing to remove */
+ luaL_setn(L, 1, e - 1); /* t.n = n-1 */
+ lua_rawgeti(L, 1, pos); /* result = t[pos] */
+ for ( ;pos<e; pos++) {
+ lua_rawgeti(L, 1, pos+1);
+ lua_rawseti(L, 1, pos); /* t[pos] = t[pos+1] */
+ }
+ lua_pushnil(L);
+ lua_rawseti(L, 1, e); /* t[e] = nil */
+ return 1;
+}
+
+
+static int tconcat (lua_State *L) {
+ luaL_Buffer b;
+ size_t lsep;
+ int i, last;
+ const char *sep = luaL_optlstring(L, 2, "", &lsep);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ i = luaL_optint(L, 3, 1);
+ last = luaL_opt(L, luaL_checkint, 4, luaL_getn(L, 1));
+ luaL_buffinit(L, &b);
+ for (; i <= last; i++) {
+ lua_rawgeti(L, 1, i);
+ luaL_argcheck(L, lua_isstring(L, -1), 1, "table contains non-strings");
+ luaL_addvalue(&b);
+ if (i != last)
+ luaL_addlstring(&b, sep, lsep);
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on `Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+*/
+
+
+static void set2 (lua_State *L, int i, int j) {
+ lua_rawseti(L, 1, i);
+ lua_rawseti(L, 1, j);
+}
+
+static int sort_comp (lua_State *L, int a, int b) {
+ if (!lua_isnil(L, 2)) { /* function? */
+ int res;
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
+ lua_call(L, 2, 1);
+ res = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+ }
+ else /* a < b? */
+ return lua_lessthan(L, a, b);
+}
+
+static void auxsort (lua_State *L, int l, int u) {
+ while (l < u) { /* for tail recursion */
+ int i, j;
+ /* sort elements a[l], a[(l+u)/2] and a[u] */
+ lua_rawgeti(L, 1, l);
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
+ set2(L, l, u); /* swap a[l] - a[u] */
+ else
+ lua_pop(L, 2);
+ if (u-l == 1) break; /* only 2 elements */
+ i = (l+u)/2;
+ lua_rawgeti(L, 1, i);
+ lua_rawgeti(L, 1, l);
+ if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */
+ set2(L, i, l);
+ else {
+ lua_pop(L, 1); /* remove a[l] */
+ lua_rawgeti(L, 1, u);
+ if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
+ set2(L, i, u);
+ else
+ lua_pop(L, 2);
+ }
+ if (u-l == 2) break; /* only 3 elements */
+ lua_rawgeti(L, 1, i); /* Pivot */
+ lua_pushvalue(L, -1);
+ lua_rawgeti(L, 1, u-1);
+ set2(L, i, u-1);
+ /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
+ i = l; j = u-1;
+ for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
+ /* repeat ++i until a[i] >= P */
+ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (i>u) luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* repeat --j until a[j] <= P */
+ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (j<l) luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ if (j<i) {
+ lua_pop(L, 3); /* pop pivot, a[i], a[j] */
+ break;
+ }
+ set2(L, i, j);
+ }
+ lua_rawgeti(L, 1, u-1);
+ lua_rawgeti(L, 1, i);
+ set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
+ /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
+ /* adjust so that smaller half is in [j..i] and larger one in [l..u] */
+ if (i-l < u-i) {
+ j=l; i=i-1; l=i+2;
+ }
+ else {
+ j=i+1; i=u; u=j-2;
+ }
+ auxsort(L, j, i); /* call recursively the smaller one */
+ } /* repeat the routine for the larger one */
+}
+
+static int sort (lua_State *L) {
+ int n = aux_getn(L, 1);
+ luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */
+ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+ lua_settop(L, 2); /* make sure there is two arguments */
+ auxsort(L, 1, n);
+ return 0;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg tab_funcs[] = {
+ {"concat", tconcat},
+ {"foreach", foreach},
+ {"foreachi", foreachi},
+ {"getn", getn},
+ {"maxn", maxn},
+ {"insert", tinsert},
+ {"remove", tremove},
+ {"setn", setn},
+ {"sort", sort},
+ {NULL, NULL}
+};
+
+
+LUALIB_API int luaopen_table (lua_State *L) {
+ luaL_register(L, LUA_TABLIBNAME, tab_funcs);
+ return 1;
+}
+
diff --git a/engines/sword25/util/lua/ltm.c b/engines/sword25/util/lua/ltm.c
new file mode 100644
index 0000000000..c27f0f6fab
--- /dev/null
+++ b/engines/sword25/util/lua/ltm.c
@@ -0,0 +1,75 @@
+/*
+** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define ltm_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+
+const char *const luaT_typenames[] = {
+ "nil", "boolean", "userdata", "number",
+ "string", "table", "function", "userdata", "thread",
+ "proto", "upval"
+};
+
+
+void luaT_init (lua_State *L) {
+ static const char *const luaT_eventname[] = { /* ORDER TM */
+ "__index", "__newindex",
+ "__gc", "__mode", "__eq",
+ "__add", "__sub", "__mul", "__div", "__mod",
+ "__pow", "__unm", "__len", "__lt", "__le",
+ "__concat", "__call"
+ };
+ int i;
+ for (i=0; i<TM_N; i++) {
+ G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
+ luaS_fix(G(L)->tmname[i]); /* never collect these names */
+ }
+}
+
+
+/*
+** function to be used with macro "fasttm": optimized for absence of
+** tag methods
+*/
+const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
+ const TValue *tm = luaH_getstr(events, ename);
+ lua_assert(event <= TM_EQ);
+ if (ttisnil(tm)) { /* no tag method? */
+ events->flags |= cast_byte(1u<<event); /* cache this fact */
+ return NULL;
+ }
+ else return tm;
+}
+
+
+const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
+ Table *mt;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ mt = hvalue(o)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(o)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(o)];
+ }
+ return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);
+}
+
diff --git a/engines/sword25/util/lua/ltm.h b/engines/sword25/util/lua/ltm.h
new file mode 100644
index 0000000000..64343b781b
--- /dev/null
+++ b/engines/sword25/util/lua/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_EQ, /* last tag method with `fast' access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_MOD,
+ TM_POW,
+ TM_UNM,
+ TM_LEN,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+LUAI_DATA const char *const luaT_typenames[];
+
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+#endif
diff --git a/engines/sword25/util/lua/lua.c b/engines/sword25/util/lua/lua.c
new file mode 100644
index 0000000000..3a46609328
--- /dev/null
+++ b/engines/sword25/util/lua/lua.c
@@ -0,0 +1,392 @@
+/*
+** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lua_c
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+static lua_State *globalL = NULL;
+
+static const char *progname = LUA_PROGNAME;
+
+
+
+static void lstop (lua_State *L, lua_Debug *ar) {
+ (void)ar; /* unused arg. */
+ lua_sethook(L, NULL, 0, 0);
+ luaL_error(L, "interrupted!");
+}
+
+
+static void laction (int i) {
+ signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
+ terminate process (default action) */
+ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
+}
+
+
+static void print_usage (void) {
+ fprintf(stderr,
+ "usage: %s [options] [script [args]].\n"
+ "Available options are:\n"
+ " -e stat execute string " LUA_QL("stat") "\n"
+ " -l name require library " LUA_QL("name") "\n"
+ " -i enter interactive mode after executing " LUA_QL("script") "\n"
+ " -v show version information\n"
+ " -- stop handling options\n"
+ " - execute stdin and stop handling options\n"
+ ,
+ progname);
+ fflush(stderr);
+}
+
+
+static void l_message (const char *pname, const char *msg) {
+ if (pname) fprintf(stderr, "%s: ", pname);
+ fprintf(stderr, "%s\n", msg);
+ fflush(stderr);
+}
+
+
+static int report (lua_State *L, int status) {
+ if (status && !lua_isnil(L, -1)) {
+ const char *msg = lua_tostring(L, -1);
+ if (msg == NULL) msg = "(error object is not a string)";
+ l_message(progname, msg);
+ lua_pop(L, 1);
+ }
+ return status;
+}
+
+
+static int traceback (lua_State *L) {
+ if (!lua_isstring(L, 1)) /* 'message' not a string? */
+ return 1; /* keep it intact */
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue(L, 1); /* pass error message */
+ lua_pushinteger(L, 2); /* skip this function and traceback */
+ lua_call(L, 2, 1); /* call debug.traceback */
+ return 1;
+}
+
+
+static int docall (lua_State *L, int narg, int clear) {
+ int status;
+ int base = lua_gettop(L) - narg; /* function index */
+ lua_pushcfunction(L, traceback); /* push traceback function */
+ lua_insert(L, base); /* put it under chunk and args */
+ signal(SIGINT, laction);
+ status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
+ signal(SIGINT, SIG_DFL);
+ lua_remove(L, base); /* remove traceback function */
+ /* force a complete garbage collection in case of errors */
+ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
+ return status;
+}
+
+
+static void print_version (void) {
+ l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT);
+}
+
+
+static int getargs (lua_State *L, char **argv, int n) {
+ int narg;
+ int i;
+ int argc = 0;
+ while (argv[argc]) argc++; /* count total number of arguments */
+ narg = argc - (n + 1); /* number of arguments to the script */
+ luaL_checkstack(L, narg + 3, "too many arguments to script");
+ for (i=n+1; i < argc; i++)
+ lua_pushstring(L, argv[i]);
+ lua_createtable(L, narg, n + 1);
+ for (i=0; i < argc; i++) {
+ lua_pushstring(L, argv[i]);
+ lua_rawseti(L, -2, i - n);
+ }
+ return narg;
+}
+
+
+static int dofile (lua_State *L, const char *name) {
+ int status = luaL_loadfile(L, name) || docall(L, 0, 1);
+ return report(L, status);
+}
+
+
+static int dostring (lua_State *L, const char *s, const char *name) {
+ int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
+ return report(L, status);
+}
+
+
+static int dolibrary (lua_State *L, const char *name) {
+ lua_getglobal(L, "require");
+ lua_pushstring(L, name);
+ return report(L, docall(L, 1, 1));
+}
+
+
+static const char *get_prompt (lua_State *L, int firstline) {
+ const char *p;
+ lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
+ p = lua_tostring(L, -1);
+ if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
+ lua_pop(L, 1); /* remove global */
+ return p;
+}
+
+
+static int incomplete (lua_State *L, int status) {
+ if (status == LUA_ERRSYNTAX) {
+ size_t lmsg;
+ const char *msg = lua_tolstring(L, -1, &lmsg);
+ const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
+ if (strstr(msg, LUA_QL("<eof>")) == tp) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ return 0; /* else... */
+}
+
+
+static int pushline (lua_State *L, int firstline) {
+ char buffer[LUA_MAXINPUT];
+ char *b = buffer;
+ size_t l;
+ const char *prmt = get_prompt(L, firstline);
+ if (lua_readline(L, b, prmt) == 0)
+ return 0; /* no input */
+ l = strlen(b);
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[l-1] = '\0'; /* remove it */
+ if (firstline && b[0] == '=') /* first line starts with `=' ? */
+ lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
+ else
+ lua_pushstring(L, b);
+ lua_freeline(L, b);
+ return 1;
+}
+
+
+static int loadline (lua_State *L) {
+ int status;
+ lua_settop(L, 0);
+ if (!pushline(L, 1))
+ return -1; /* no input */
+ for (;;) { /* repeat until gets a complete line */
+ status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
+ if (!incomplete(L, status)) break; /* cannot try to add lines? */
+ if (!pushline(L, 0)) /* no more input? */
+ return -1;
+ lua_pushliteral(L, "\n"); /* add a new line... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+ lua_saveline(L, 1);
+ lua_remove(L, 1); /* remove line */
+ return status;
+}
+
+
+static void dotty (lua_State *L) {
+ int status;
+ const char *oldprogname = progname;
+ progname = NULL;
+ while ((status = loadline(L)) != -1) {
+ if (status == 0) status = docall(L, 0, 0);
+ report(L, status);
+ if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
+ l_message(progname, lua_pushfstring(L,
+ "error calling " LUA_QL("print") " (%s)",
+ lua_tostring(L, -1)));
+ }
+ }
+ lua_settop(L, 0); /* clear stack */
+ fputs("\n", stdout);
+ fflush(stdout);
+ progname = oldprogname;
+}
+
+
+static int handle_script (lua_State *L, char **argv, int n) {
+ int status;
+ const char *fname;
+ int narg = getargs(L, argv, n); /* collect arguments */
+ lua_setglobal(L, "arg");
+ fname = argv[n];
+ if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
+ fname = NULL; /* stdin */
+ status = luaL_loadfile(L, fname);
+ lua_insert(L, -(narg+1));
+ if (status == 0)
+ status = docall(L, narg, 0);
+ else
+ lua_pop(L, narg);
+ return report(L, status);
+}
+
+
+/* check that argument has no extra characters at the end */
+#define notail(x) {if ((x)[2] != '\0') return -1;}
+
+
+static int collectargs (char **argv, int *pi, int *pv, int *pe) {
+ int i;
+ for (i = 1; argv[i] != NULL; i++) {
+ if (argv[i][0] != '-') /* not an option? */
+ return i;
+ switch (argv[i][1]) { /* option */
+ case '-':
+ notail(argv[i]);
+ return (argv[i+1] != NULL ? i+1 : 0);
+ case '\0':
+ return i;
+ case 'i':
+ notail(argv[i]);
+ *pi = 1; /* go through */
+ case 'v':
+ notail(argv[i]);
+ *pv = 1;
+ break;
+ case 'e':
+ *pe = 1; /* go through */
+ case 'l':
+ if (argv[i][2] == '\0') {
+ i++;
+ if (argv[i] == NULL) return -1;
+ }
+ break;
+ default: return -1; /* invalid option */
+ }
+ }
+ return 0;
+}
+
+
+static int runargs (lua_State *L, char **argv, int n) {
+ int i;
+ for (i = 1; i < n; i++) {
+ if (argv[i] == NULL) continue;
+ lua_assert(argv[i][0] == '-');
+ switch (argv[i][1]) { /* option */
+ case 'e': {
+ const char *chunk = argv[i] + 2;
+ if (*chunk == '\0') chunk = argv[++i];
+ lua_assert(chunk != NULL);
+ if (dostring(L, chunk, "=(command line)") != 0)
+ return 1;
+ break;
+ }
+ case 'l': {
+ const char *filename = argv[i] + 2;
+ if (*filename == '\0') filename = argv[++i];
+ lua_assert(filename != NULL);
+ if (dolibrary(L, filename))
+ return 1; /* stop if file fails */
+ break;
+ }
+ default: break;
+ }
+ }
+ return 0;
+}
+
+
+static int handle_luainit (lua_State *L) {
+ const char *init = getenv(LUA_INIT);
+ if (init == NULL) return 0; /* status OK */
+ else if (init[0] == '@')
+ return dofile(L, init+1);
+ else
+ return dostring(L, init, "=" LUA_INIT);
+}
+
+
+struct Smain {
+ int argc;
+ char **argv;
+ int status;
+};
+
+
+static int pmain (lua_State *L) {
+ struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
+ char **argv = s->argv;
+ int script;
+ int has_i = 0, has_v = 0, has_e = 0;
+ globalL = L;
+ if (argv[0] && argv[0][0]) progname = argv[0];
+ lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
+ luaL_openlibs(L); /* open libraries */
+ lua_gc(L, LUA_GCRESTART, 0);
+ s->status = handle_luainit(L);
+ if (s->status != 0) return 0;
+ script = collectargs(argv, &has_i, &has_v, &has_e);
+ if (script < 0) { /* invalid args? */
+ print_usage();
+ s->status = 1;
+ return 0;
+ }
+ if (has_v) print_version();
+ s->status = runargs(L, argv, (script > 0) ? script : s->argc);
+ if (s->status != 0) return 0;
+ if (script)
+ s->status = handle_script(L, argv, script);
+ if (s->status != 0) return 0;
+ if (has_i)
+ dotty(L);
+ else if (script == 0 && !has_e && !has_v) {
+ if (lua_stdin_is_tty()) {
+ print_version();
+ dotty(L);
+ }
+ else dofile(L, NULL); /* executes stdin as a file */
+ }
+ return 0;
+}
+
+
+int main (int argc, char **argv) {
+ int status;
+ struct Smain s;
+ lua_State *L = lua_open(); /* create state */
+ if (L == NULL) {
+ l_message(argv[0], "cannot create state: not enough memory");
+ return EXIT_FAILURE;
+ }
+ s.argc = argc;
+ s.argv = argv;
+ status = lua_cpcall(L, &pmain, &s);
+ report(L, status);
+ lua_close(L);
+ return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/engines/sword25/util/lua/lua.h b/engines/sword25/util/lua/lua.h
new file mode 100644
index 0000000000..5bc97b746f
--- /dev/null
+++ b/engines/sword25/util/lua/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** Lua - An Extensible Extension Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION "Lua 5.1"
+#define LUA_RELEASE "Lua 5.1.3"
+#define LUA_VERSION_NUM 501
+#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
+
+
+/* mark for precompiled code (`<esc>Lua') */
+#define LUA_SIGNATURE "\033Lua"
+
+/* option for multiple returns in `lua_pcall' and `lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** pseudo-indices
+*/
+#define LUA_REGISTRYINDEX (-10000)
+#define LUA_ENVIRONINDEX (-10001)
+#define LUA_GLOBALSINDEX (-10002)
+#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
+
+
+/* thread status; 0 is OK */
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+
+/*
+** functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
+
+
+/*
+** prototype for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_remove) (lua_State *L, int idx);
+LUA_API void (lua_insert) (lua_State *L, int idx);
+LUA_API void (lua_replace) (lua_State *L, int idx);
+LUA_API int (lua_checkstack) (lua_State *L, int sz);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
+
+LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
+LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_objlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void (lua_gettable) (lua_State *L, int idx);
+LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawget) (lua_State *L, int idx);
+LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_getfenv) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setfenv) (lua_State *L, int idx);
+
+
+/*
+** `load' and `call' functions (load and run Lua code)
+*/
+LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
+LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
+LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yield) (lua_State *L, int nresults);
+LUA_API int (lua_resume) (lua_State *L, int narg);
+LUA_API int (lua_status) (lua_State *L);
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_strlen(L,i) lua_objlen(L, (i))
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) \
+ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
+
+#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
+#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+
+/*
+** compatibility macros and functions
+*/
+
+#define lua_open() luaL_newstate()
+
+#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
+
+#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
+
+#define lua_Chunkreader lua_Reader
+#define lua_Chunkwriter lua_Writer
+
+
+/* hack */
+LUA_API void lua_setlevel (lua_State *from, lua_State *to);
+
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILRET 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debuger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
+
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook lua_gethook (lua_State *L);
+LUA_API int lua_gethookmask (lua_State *L);
+LUA_API int lua_gethookcount (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `local', `field', `method' */
+ const char *what; /* (S) `Lua', `C', `main', `tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ int i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. 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, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* 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.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/engines/sword25/util/lua/luac.c b/engines/sword25/util/lua/luac.c
new file mode 100644
index 0000000000..d07017391b
--- /dev/null
+++ b/engines/sword25/util/lua/luac.c
@@ -0,0 +1,200 @@
+/*
+** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $
+** Lua compiler (saves bytecodes to files; also list bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstring.h"
+#include "lundump.h"
+
+#define PROGNAME "luac" /* default program name */
+#define OUTPUT PROGNAME ".out" /* default output file */
+
+static int listing=0; /* list bytecodes? */
+static int dumping=1; /* dump bytecodes? */
+static int stripping=0; /* strip debug information? */
+static char Output[]={ OUTPUT }; /* default output file name */
+static const char* output=Output; /* actual output file name */
+static const char* progname=PROGNAME; /* actual program name */
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+ fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message);
+ else
+ fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+ "usage: %s [options] [filenames].\n"
+ "Available options are:\n"
+ " - process stdin\n"
+ " -l list\n"
+ " -o name output to file " LUA_QL("name") " (default is \"%s\")\n"
+ " -p parse only\n"
+ " -s strip debug information\n"
+ " -v show version information\n"
+ " -- stop handling options\n",
+ progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s) (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+ if (*argv[i]!='-') /* end of options; keep it */
+ break;
+ else if (IS("--")) /* end of options; skip it */
+ {
+ ++i;
+ if (version) ++version;
+ break;
+ }
+ else if (IS("-")) /* end of options; use stdin */
+ break;
+ else if (IS("-l")) /* list */
+ ++listing;
+ else if (IS("-o")) /* output file */
+ {
+ output=argv[++i];
+ if (output==NULL || *output==0) usage(LUA_QL("-o") " needs argument");
+ if (IS("-")) output=NULL;
+ }
+ else if (IS("-p")) /* parse only */
+ dumping=0;
+ else if (IS("-s")) /* strip debug information */
+ stripping=1;
+ else if (IS("-v")) /* show version */
+ ++version;
+ else /* unknown option */
+ usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+ dumping=0;
+ argv[--i]=Output;
+ }
+ if (version)
+ {
+ printf("%s %s\n",LUA_RELEASE,LUA_COPYRIGHT);
+ if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+#define toproto(L,i) (clvalue(L->top+(i))->l.p)
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+ return toproto(L,-1);
+ else
+ {
+ int i,pc;
+ Proto* f=luaF_newproto(L);
+ setptvalue2s(L,L->top,f); incr_top(L);
+ f->source=luaS_newliteral(L,"=(" PROGNAME ")");
+ f->maxstacksize=1;
+ pc=2*n+1;
+ f->code=luaM_newvector(L,pc,Instruction);
+ f->sizecode=pc;
+ f->p=luaM_newvector(L,n,Proto*);
+ f->sizep=n;
+ pc=0;
+ for (i=0; i<n; i++)
+ {
+ f->p[i]=toproto(L,i-n-1);
+ f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
+ f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
+ }
+ f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
+ return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+struct Smain {
+ int argc;
+ char** argv;
+};
+
+static int pmain(lua_State* L)
+{
+ struct Smain* s = (struct Smain*)lua_touserdata(L, 1);
+ int argc=s->argc;
+ char** argv=s->argv;
+ const Proto* f;
+ int i;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i<argc; i++)
+ {
+ const char* filename=IS("-") ? NULL : argv[i];
+ if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+ FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+ if (D==NULL) cannot("open");
+ lua_lock(L);
+ luaU_dump(L,f,writer,D,stripping);
+ lua_unlock(L);
+ if (ferror(D)) cannot("write");
+ if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ struct Smain s;
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=lua_open();
+ if (L==NULL) fatal("not enough memory for state");
+ s.argc=argc;
+ s.argv=argv;
+ if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
diff --git a/engines/sword25/util/lua/luaconf.h b/engines/sword25/util/lua/luaconf.h
new file mode 100644
index 0000000000..d4eb2c9cd4
--- /dev/null
+++ b/engines/sword25/util/lua/luaconf.h
@@ -0,0 +1,763 @@
+/*
+** $Id: luaconf.h,v 1.82.1.6 2008/01/18 17:07:48 roberto Exp $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lconfig_h
+#define lconfig_h
+
+#include <limits.h>
+#include <stddef.h>
+
+
+/*
+** ==================================================================
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+@@ LUA_ANSI controls the use of non-ansi features.
+** CHANGE it (define it) if you want Lua to avoid the use of any
+** non-ansi feature or library.
+*/
+#if defined(__STRICT_ANSI__)
+#define LUA_ANSI
+#endif
+
+
+#if !defined(LUA_ANSI) && defined(_WIN32)
+#define LUA_WIN
+#endif
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#define LUA_USE_READLINE /* needs some extra libraries */
+#endif
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_DL_DYLD /* does not need extra library */
+#endif
+
+
+
+/*
+@@ LUA_USE_POSIX includes all functionallity listed as X/Open System
+@* Interfaces Extension (XSI).
+** CHANGE it (define it) if your system is XSI compatible.
+*/
+#if defined(LUA_USE_POSIX)
+#define LUA_USE_MKSTEMP
+#define LUA_USE_ISATTY
+#define LUA_USE_POPEN
+#define LUA_USE_ULONGJMP
+#endif
+
+
+/*
+@@ LUA_PATH and LUA_CPATH are the names of the environment variables that
+@* Lua check to set its paths.
+@@ LUA_INIT is the name of the environment variable that Lua
+@* checks for initialization code.
+** CHANGE them if you want different names.
+*/
+#define LUA_PATH "LUA_PATH"
+#define LUA_CPATH "LUA_CPATH"
+#define LUA_INIT "LUA_INIT"
+
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+@* Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+@* C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+#if defined(_WIN32)
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+#define LUA_PATH_DEFAULT \
+ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
+#define LUA_CPATH_DEFAULT \
+ ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
+
+#else
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/5.1/"
+#define LUA_CDIR LUA_ROOT "lib/lua/5.1/"
+#define LUA_PATH_DEFAULT \
+ "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua"
+#define LUA_CPATH_DEFAULT \
+ "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
+#endif
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+
+/*
+@@ LUA_PATHSEP is the character that separates templates in a path.
+@@ LUA_PATH_MARK is the string that marks the substitution points in a
+@* template.
+@@ LUA_EXECDIR in a Windows path is replaced by the executable's
+@* directory.
+@@ LUA_IGMARK is a mark to ignore all before it when bulding the
+@* luaopen_ function name.
+** CHANGE them if for some reason your system cannot use those
+** characters. (E.g., if one of those characters is a common character
+** in file/directory names.) Probably you do not need to change them.
+*/
+#define LUA_PATHSEP ";"
+#define LUA_PATH_MARK "?"
+#define LUA_EXECDIR "!"
+#define LUA_IGMARK "-"
+
+
+/*
+@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger.
+** CHANGE that if ptrdiff_t is not adequate on your machine. (On most
+** machines, ptrdiff_t gives a good choice between int or long.)
+*/
+#define LUA_INTEGER ptrdiff_t
+
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all standard library functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL)
+
+#if defined(LUA_CORE) || defined(LUA_LIB)
+#define LUA_API __declspec(dllexport)
+#else
+#define LUA_API __declspec(dllimport)
+#endif
+
+#else
+
+#define LUA_API extern
+
+#endif
+
+/* more often than not the libs go together with the core */
+#define LUALIB_API LUA_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+@* exported to outside modules.
+@@ LUAI_DATA is a mark for all extern (const) variables that are not to
+@* be exported to outside modules.
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library.
+*/
+#if defined(luaall_c)
+#define LUAI_FUNC static
+#define LUAI_DATA /* empty */
+
+#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__)
+#define LUAI_FUNC __attribute__((visibility("hidden"))) extern
+#define LUAI_DATA LUAI_FUNC
+
+#else
+#define LUAI_FUNC extern
+#define LUAI_DATA extern
+#endif
+
+
+
+/*
+@@ LUA_QL describes how error messages quote program elements.
+** CHANGE it if you want a different appearance.
+*/
+#define LUA_QL(x) "'" x "'"
+#define LUA_QS LUA_QL("%s")
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+@* of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+** {==================================================================
+** Stand-alone configuration
+** ===================================================================
+*/
+
+#if defined(lua_c) || defined(luaall_c)
+
+/*
+@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+@* is, whether we're running lua interactively).
+** CHANGE it if you have a better definition for non-POSIX/non-Windows
+** systems.
+*/
+#if defined(LUA_USE_ISATTY)
+#include <unistd.h>
+#define lua_stdin_is_tty() isatty(0)
+#elif defined(LUA_WIN)
+#include <io.h>
+#include <stdio.h>
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+#else
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+#endif
+
+
+/*
+@@ LUA_PROMPT is the default prompt used by stand-alone Lua.
+@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.
+** CHANGE them if you want different prompts. (You can also change the
+** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)
+*/
+#define LUA_PROMPT "> "
+#define LUA_PROMPT2 ">> "
+
+
+/*
+@@ LUA_PROGNAME is the default name for the stand-alone Lua program.
+** CHANGE it if your stand-alone interpreter has a different name and
+** your system is not able to detect that name automatically.
+*/
+#define LUA_PROGNAME "lua"
+
+
+/*
+@@ LUA_MAXINPUT is the maximum length for an input line in the
+@* stand-alone interpreter.
+** CHANGE it if you need longer lines.
+*/
+#define LUA_MAXINPUT 512
+
+
+/*
+@@ lua_readline defines how to show a prompt and then read a line from
+@* the standard input.
+@@ lua_saveline defines how to "save" a read line in a "history".
+@@ lua_freeline defines how to free a line read by lua_readline.
+** CHANGE them if you want to improve this functionality (e.g., by using
+** GNU readline and history facilities).
+*/
+#if defined(LUA_USE_READLINE)
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,idx) \
+ if (lua_strlen(L,idx) > 0) /* non-empty line? */ \
+ add_history(lua_tostring(L, idx)); /* add it to history */
+#define lua_freeline(L,b) ((void)L, free(b))
+#else
+#define lua_readline(L,b,p) \
+ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
+ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
+#define lua_saveline(L,idx) { (void)L; (void)idx; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+#endif
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles
+@* as a percentage.
+** CHANGE it if you want the GC to run faster or slower (higher values
+** mean larger pauses which mean slower collection.) You can also change
+** this value dynamically.
+*/
+#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */
+
+
+/*
+@@ LUAI_GCMUL defines the default speed of garbage collection relative to
+@* memory allocation as a percentage.
+** CHANGE it if you want to change the granularity of the garbage
+** collection. (Higher values mean coarser collections. 0 represents
+** infinity, where each step performs a full collection.) You can also
+** change this value dynamically.
+*/
+#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */
+
+
+
+/*
+@@ LUA_COMPAT_GETN controls compatibility with old getn behavior.
+** CHANGE it (define it) if you want exact compatibility with the
+** behavior of setn/getn in Lua 5.0.
+*/
+#define LUA_COMPAT_GETN // BS25 #undef LUA_COMPAT_GETN
+
+/*
+@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib.
+** CHANGE it to undefined as soon as you do not need a global 'loadlib'
+** function (the function is still available as 'package.loadlib').
+*/
+#undef LUA_COMPAT_LOADLIB
+
+/*
+@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature.
+** CHANGE it to undefined as soon as your programs use only '...' to
+** access vararg parameters (instead of the old 'arg' table).
+*/
+#define LUA_COMPAT_VARARG
+
+/*
+@@ LUA_COMPAT_MOD controls compatibility with old math.mod function.
+** CHANGE it to undefined as soon as your programs use 'math.fmod' or
+** the new '%' operator instead of 'math.mod'.
+*/
+#define LUA_COMPAT_MOD
+
+/*
+@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting
+@* facility.
+** CHANGE it to 2 if you want the old behaviour, or undefine it to turn
+** off the advisory error when nesting [[...]].
+*/
+#define LUA_COMPAT_LSTR 1
+
+/*
+@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name.
+** CHANGE it to undefined as soon as you rename 'string.gfind' to
+** 'string.gmatch'.
+*/
+#define LUA_COMPAT_GFIND
+
+/*
+@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib'
+@* behavior.
+** CHANGE it to undefined as soon as you replace to 'luaL_register'
+** your uses of 'luaL_openlib'
+*/
+#define LUA_COMPAT_OPENLIB
+
+
+
+/*
+@@ luai_apicheck is the assert macro used by the Lua-C API.
+** CHANGE luai_apicheck if you want Lua to perform some checks in the
+** parameters it gets from API calls. This may slow down the interpreter
+** a bit, but may be quite useful when debugging C code that interfaces
+** with Lua. A useful redefinition is to use assert.h.
+*/
+#if defined(LUA_USE_APICHECK)
+#include <assert.h>
+#define luai_apicheck(L,o) { (void)L; assert(o); }
+#else
+#define luai_apicheck(L,o) { (void)L; }
+#endif
+
+
+/*
+@@ LUAI_BITSINT defines the number of bits in an int.
+** CHANGE here if Lua cannot automatically detect the number of bits of
+** your machine. Probably you do not need to change this.
+*/
+/* avoid overflows in comparison */
+#if INT_MAX-20 < 32760
+#define LUAI_BITSINT 16
+#elif INT_MAX > 2147483640L
+/* int has at least 32 bits */
+#define LUAI_BITSINT 32
+#else
+#error "you must define LUA_BITSINT with number of bits in an integer"
+#endif
+
+
+/*
+@@ LUAI_UINT32 is an unsigned integer with at least 32 bits.
+@@ LUAI_INT32 is an signed integer with at least 32 bits.
+@@ LUAI_UMEM is an unsigned integer big enough to count the total
+@* memory used by Lua.
+@@ LUAI_MEM is a signed integer big enough to count the total memory
+@* used by Lua.
+** CHANGE here if for some weird reason the default definitions are not
+** good enough for your machine. (The definitions in the 'else'
+** part always works, but may waste space on machines with 64-bit
+** longs.) Probably you do not need to change this.
+*/
+#if LUAI_BITSINT >= 32
+#define LUAI_UINT32 unsigned int
+#define LUAI_INT32 int
+#define LUAI_MAXINT32 INT_MAX
+#define LUAI_UMEM size_t
+#define LUAI_MEM ptrdiff_t
+#else
+/* 16-bit ints */
+#define LUAI_UINT32 unsigned long
+#define LUAI_INT32 long
+#define LUAI_MAXINT32 LONG_MAX
+#define LUAI_UMEM unsigned long
+#define LUAI_MEM long
+#endif
+
+
+/*
+@@ LUAI_MAXCALLS limits the number of nested calls.
+** CHANGE it if you need really deep recursive calls. This limit is
+** arbitrary; its only purpose is to stop infinite recursion before
+** exhausting memory.
+*/
+#define LUAI_MAXCALLS 20000
+
+
+/*
+@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function
+@* can use.
+** CHANGE it if you need lots of (Lua) stack space for your C
+** functions. This limit is arbitrary; its only purpose is to stop C
+** functions to consume unlimited stack space.
+*/
+#define LUAI_MCS_AUX ((int)(INT_MAX / (4*sizeof(LUA_NUMBER))))
+#define LUAI_MAXCSTACK (LUAI_MCS_AUX > SHRT_MAX ? SHRT_MAX : LUAI_MCS_AUX)
+
+
+
+/*
+** {==================================================================
+** CHANGE (to smaller values) the following definitions if your system
+** has a small C stack. (Or you may want to change them to larger
+** values if your system has a large C stack and these limits are
+** too rigid for you.) Some of these constants control the size of
+** stack-allocated arrays used by the compiler or the interpreter, while
+** others limit the maximum number of recursive calls that the compiler
+** or the interpreter can perform. Values too large may cause a C stack
+** overflow for some forms of deep constructs.
+** ===================================================================
+*/
+
+
+/*
+@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and
+@* syntactical nested non-terminals in a program.
+*/
+#define LUAI_MAXCCALLS 200
+
+
+/*
+@@ LUAI_MAXVARS is the maximum number of local variables per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXVARS 200
+
+
+/*
+@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function
+@* (must be smaller than 250).
+*/
+#define LUAI_MAXUPVALUES 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
+*/
+#define LUAL_BUFFERSIZE BUFSIZ
+
+/* }================================================================== */
+
+
+
+
+/*
+** {==================================================================
+@@ LUA_NUMBER is the type of numbers in Lua.
+** CHANGE the following definitions only if you want to build Lua
+** with a number type different from double. You may also need to
+** change lua_number2int & lua_number2integer.
+** ===================================================================
+*/
+
+#define LUA_NUMBER_DOUBLE
+#define LUA_NUMBER double
+
+/*
+@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'
+@* over a number.
+*/
+#define LUAI_UACNUMBER double
+
+
+/*
+@@ LUA_NUMBER_SCAN is the format for reading numbers.
+@@ LUA_NUMBER_FMT is the format for writing numbers.
+@@ lua_number2str converts a number to a string.
+@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion.
+@@ lua_str2number converts a string to a number.
+*/
+#define LUA_NUMBER_SCAN "%lf"
+#define LUA_NUMBER_FMT "%.14g"
+#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n))
+#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */
+#define lua_str2number(s,p) strtod((s), (p))
+
+
+/*
+@@ The luai_num* macros define the primitive operations over numbers.
+*/
+#if defined(LUA_CORE)
+#include <math.h>
+#define luai_numadd(a,b) ((a)+(b))
+#define luai_numsub(a,b) ((a)-(b))
+#define luai_nummul(a,b) ((a)*(b))
+#define luai_numdiv(a,b) ((a)/(b))
+#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
+#define luai_numpow(a,b) (pow(a,b))
+#define luai_numunm(a) (-(a))
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numlt(a,b) ((a)<(b))
+#define luai_numle(a,b) ((a)<=(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+#endif
+
+
+/*
+@@ lua_number2int is a macro to convert lua_Number to int.
+@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.
+** CHANGE them if you know a faster way to convert a lua_Number to
+** int (with any rounding method and without throwing errors) in your
+** system. In Pentium machines, a naive typecast from double to int
+** in C is extremely slow, so any alternative is worth trying.
+*/
+
+/* On a Pentium, resort to a trick */
+#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \
+ (defined(__i386) || defined (_M_IX86) || defined(__i386__))
+
+/* On a Microsoft compiler, use assembler */
+#if defined(_MSC_VER)
+
+#define lua_number2int(i,d) __asm fld d __asm fistp i
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+/* the next trick should work on any Pentium, but sometimes clashes
+ with a DirectX idiosyncrasy */
+#else
+
+union luai_Cast { double l_d; long l_l; };
+#define lua_number2int(i,d) \
+ { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }
+#define lua_number2integer(i,n) lua_number2int(i, n)
+
+#endif
+
+
+/* this option always works, but may be slow */
+#else
+#define lua_number2int(i,d) ((i)=(int)(d))
+#define lua_number2integer(i,d) ((i)=(lua_Integer)(d))
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
+** CHANGE it if your system requires alignments larger than double. (For
+** instance, if your system supports long doubles and they must be
+** aligned in 16-byte boundaries, then you should add long double in the
+** union.) Probably you do not need to change this.
+*/
+#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; }
+
+
+/*
+@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.
+** CHANGE them if you prefer to use longjmp/setjmp even with C++
+** or if want/don't to use _longjmp/_setjmp instead of regular
+** longjmp/setjmp. By default, Lua handles errors with exceptions when
+** compiling as C++ code, with _longjmp/_setjmp when asked to use them,
+** and with longjmp/setjmp otherwise.
+*/
+#if 0 /* defined(__cplusplus) */
+/* C++ exceptions */
+#define LUAI_THROW(L,c) throw(c)
+#define LUAI_TRY(L,c,a) try { a } catch(...) \
+ { if ((c)->status == 0) (c)->status = -1; }
+#define luai_jmpbuf int /* dummy variable */
+
+#elif defined(LUA_USE_ULONGJMP)
+/* in Unix, try _longjmp/_setjmp (more efficient) */
+#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#else
+/* default handling with long jumps */
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#endif
+
+
+/*
+@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern
+@* can do during pattern-matching.
+** CHANGE it if you need more captures. This limit is arbitrary.
+*/
+#define LUA_MAXCAPTURES 32
+
+
+/*
+@@ lua_tmpnam is the function that the OS library uses to create a
+@* temporary name.
+@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam.
+** CHANGE them if you have an alternative to tmpnam (which is considered
+** insecure) or if you want the original tmpnam anyway. By default, Lua
+** uses tmpnam except when POSIX is available, where it uses mkstemp.
+*/
+#if defined(loslib_c) || defined(luaall_c)
+
+#if defined(LUA_USE_MKSTEMP)
+#include <unistd.h>
+#define LUA_TMPNAMBUFSIZE 32
+#define lua_tmpnam(b,e) { \
+ strcpy(b, "/tmp/lua_XXXXXX"); \
+ e = mkstemp(b); \
+ if (e != -1) close(e); \
+ e = (e == -1); }
+
+#else
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+#endif
+
+#endif
+
+
+/*
+@@ lua_popen spawns a new process connected to the current one through
+@* the file streams.
+** CHANGE it if you have a way to implement it in your system.
+*/
+#if defined(LUA_USE_POPEN)
+
+#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m))
+#define lua_pclose(L,file) ((void)L, (pclose(file) != -1))
+
+#elif defined(LUA_WIN)
+
+#define lua_popen(L,c,m) ((void)L, _popen(c,m))
+#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1))
+
+#else
+
+#define lua_popen(L,c,m) ((void)((void)c, m), \
+ luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0)
+#define lua_pclose(L,file) ((void)((void)L, file), 0)
+
+#endif
+
+/*
+@@ LUA_DL_* define which dynamic-library system Lua should use.
+** CHANGE here if Lua has problems choosing the appropriate
+** dynamic-library system for your platform (either Windows' DLL, Mac's
+** dyld, or Unix's dlopen). If your system is some kind of Unix, there
+** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for
+** it. To use dlopen you also need to adapt the src/Makefile (probably
+** adding -ldl to the linker options), so Lua does not select it
+** automatically. (When you change the makefile to add -ldl, you must
+** also add -DLUA_USE_DLOPEN.)
+** If you do not want any kind of dynamic library, undefine all these
+** options.
+** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD.
+*/
+#if defined(LUA_USE_DLOPEN)
+#define LUA_DL_DLOPEN
+#endif
+
+#if defined(LUA_WIN)
+#define LUA_DL_DLL
+#endif
+
+
+/*
+@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State
+@* (the data goes just *before* the lua_State pointer).
+** CHANGE (define) this if you really need that. This value must be
+** a multiple of the maximum alignment required for your machine.
+*/
+#define LUAI_EXTRASPACE 0
+
+
+/*
+@@ luai_userstate* allow user-specific actions on threads.
+** CHANGE them if you defined LUAI_EXTRASPACE and need to do something
+** extra when a thread is created/deleted/resumed/yielded.
+*/
+#define luai_userstateopen(L) ((void)L)
+#define luai_userstateclose(L) ((void)L)
+#define luai_userstatethread(L,L1) ((void)L)
+#define luai_userstatefree(L) ((void)L)
+#define luai_userstateresume(L,n) ((void)L)
+#define luai_userstateyield(L,n) ((void)L)
+
+
+/*
+@@ LUA_INTFRMLEN is the length modifier for integer conversions
+@* in 'string.format'.
+@@ LUA_INTFRM_T is the integer type correspoding to the previous length
+@* modifier.
+** CHANGE them if your system supports long long or does not support long.
+*/
+
+#if defined(LUA_USELONGLONG)
+
+#define LUA_INTFRMLEN "ll"
+#define LUA_INTFRM_T long long
+
+#else
+
+#define LUA_INTFRMLEN "l"
+#define LUA_INTFRM_T long
+
+#endif
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+
+
+#endif
+
diff --git a/engines/sword25/util/lua/lualib.h b/engines/sword25/util/lua/lualib.h
new file mode 100644
index 0000000000..469417f670
--- /dev/null
+++ b/engines/sword25/util/lua/lualib.h
@@ -0,0 +1,53 @@
+/*
+** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+/* Key to file-handle type */
+#define LUA_FILEHANDLE "FILE*"
+
+
+#define LUA_COLIBNAME "coroutine"
+LUALIB_API int (luaopen_base) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUALIB_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUALIB_API int (luaopen_io) (lua_State *L);
+
+#define LUA_OSLIBNAME "os"
+LUALIB_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUALIB_API int (luaopen_string) (lua_State *L);
+
+#define LUA_MATHLIBNAME "math"
+LUALIB_API int (luaopen_math) (lua_State *L);
+
+#define LUA_DBLIBNAME "debug"
+LUALIB_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUALIB_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+
+#ifndef lua_assert
+#define lua_assert(x) ((void)0)
+#endif
+
+
+#endif
diff --git a/engines/sword25/util/lua/lundump.c b/engines/sword25/util/lua/lundump.c
new file mode 100644
index 0000000000..731c064553
--- /dev/null
+++ b/engines/sword25/util/lua/lundump.c
@@ -0,0 +1,225 @@
+/*
+** $Id: lundump.c,v 2.7.1.2 2008/01/18 16:39:11 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#include <string.h>
+
+#define lundump_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstring.h"
+#include "lundump.h"
+#include "lzio.h"
+
+typedef struct {
+ lua_State* L;
+ ZIO* Z;
+ Mbuffer* b;
+ const char* name;
+} LoadState;
+
+#ifdef LUAC_TRUST_BINARIES
+#define IF(c,s)
+#define error(S,s)
+#else
+#define IF(c,s) if (c) error(S,s)
+
+static void error(LoadState* S, const char* why)
+{
+ luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why);
+ luaD_throw(S->L,LUA_ERRSYNTAX);
+}
+#endif
+
+#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size))
+#define LoadByte(S) (lu_byte)LoadChar(S)
+#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x))
+#define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
+
+static void LoadBlock(LoadState* S, void* b, size_t size)
+{
+ size_t r=luaZ_read(S->Z,b,size);
+ UNUSED(r);
+ IF (r!=0, "unexpected end");
+}
+
+static int LoadChar(LoadState* S)
+{
+ char x;
+ LoadVar(S,x);
+ return x;
+}
+
+static int LoadInt(LoadState* S)
+{
+ int x;
+ LoadVar(S,x);
+ IF (x<0, "bad integer");
+ return x;
+}
+
+static lua_Number LoadNumber(LoadState* S)
+{
+ lua_Number x;
+ LoadVar(S,x);
+ return x;
+}
+
+static TString* LoadString(LoadState* S)
+{
+ size_t size;
+ LoadVar(S,size);
+ if (size==0)
+ return NULL;
+ else
+ {
+ char* s=luaZ_openspace(S->L,S->b,size);
+ LoadBlock(S,s,size);
+ return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */
+ }
+}
+
+static void LoadCode(LoadState* S, Proto* f)
+{
+ int n=LoadInt(S);
+ f->code=luaM_newvector(S->L,n,Instruction);
+ f->sizecode=n;
+ LoadVector(S,f->code,n,sizeof(Instruction));
+}
+
+static Proto* LoadFunction(LoadState* S, TString* p);
+
+static void LoadConstants(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ f->k=luaM_newvector(S->L,n,TValue);
+ f->sizek=n;
+ for (i=0; i<n; i++) setnilvalue(&f->k[i]);
+ for (i=0; i<n; i++)
+ {
+ TValue* o=&f->k[i];
+ int t=LoadChar(S);
+ switch (t)
+ {
+ case LUA_TNIL:
+ setnilvalue(o);
+ break;
+ case LUA_TBOOLEAN:
+ setbvalue(o,LoadChar(S));
+ break;
+ case LUA_TNUMBER:
+ setnvalue(o,LoadNumber(S));
+ break;
+ case LUA_TSTRING:
+ setsvalue2n(S->L,o,LoadString(S));
+ break;
+ default:
+ error(S,"bad constant");
+ break;
+ }
+ }
+ n=LoadInt(S);
+ f->p=luaM_newvector(S->L,n,Proto*);
+ f->sizep=n;
+ for (i=0; i<n; i++) f->p[i]=NULL;
+ for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);
+}
+
+static void LoadDebug(LoadState* S, Proto* f)
+{
+ int i,n;
+ n=LoadInt(S);
+ f->lineinfo=luaM_newvector(S->L,n,int);
+ f->sizelineinfo=n;
+ LoadVector(S,f->lineinfo,n,sizeof(int));
+ n=LoadInt(S);
+ f->locvars=luaM_newvector(S->L,n,LocVar);
+ f->sizelocvars=n;
+ for (i=0; i<n; i++) f->locvars[i].varname=NULL;
+ for (i=0; i<n; i++)
+ {
+ f->locvars[i].varname=LoadString(S);
+ f->locvars[i].startpc=LoadInt(S);
+ f->locvars[i].endpc=LoadInt(S);
+ }
+ n=LoadInt(S);
+ f->upvalues=luaM_newvector(S->L,n,TString*);
+ f->sizeupvalues=n;
+ for (i=0; i<n; i++) f->upvalues[i]=NULL;
+ for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);
+}
+
+static Proto* LoadFunction(LoadState* S, TString* p)
+{
+ Proto* f=luaF_newproto(S->L);
+ setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
+ f->source=LoadString(S); if (f->source==NULL) f->source=p;
+ f->linedefined=LoadInt(S);
+ f->lastlinedefined=LoadInt(S);
+ f->nups=LoadByte(S);
+ f->numparams=LoadByte(S);
+ f->is_vararg=LoadByte(S);
+ f->maxstacksize=LoadByte(S);
+ LoadCode(S,f);
+ LoadConstants(S,f);
+ LoadDebug(S,f);
+ IF (!luaG_checkcode(f), "bad code");
+ S->L->top--;
+ return f;
+}
+
+static void LoadHeader(LoadState* S)
+{
+ char h[LUAC_HEADERSIZE];
+ char s[LUAC_HEADERSIZE];
+ luaU_header(h);
+ LoadBlock(S,s,LUAC_HEADERSIZE);
+ IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header");
+}
+
+/*
+** load precompiled chunk
+*/
+Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
+{
+ LoadState S;
+ if (*name=='@' || *name=='=')
+ S.name=name+1;
+ else if (*name==LUA_SIGNATURE[0])
+ S.name="binary string";
+ else
+ S.name=name;
+ S.L=L;
+ S.Z=Z;
+ S.b=buff;
+ LoadHeader(&S);
+ return LoadFunction(&S,luaS_newliteral(L,"=?"));
+}
+
+/*
+* make header
+*/
+void luaU_header (char* h)
+{
+ int x=1;
+ memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);
+ h+=sizeof(LUA_SIGNATURE)-1;
+ *h++=(char)LUAC_VERSION;
+ *h++=(char)LUAC_FORMAT;
+ *h++=(char)*(char*)&x; /* endianness */
+ *h++=(char)sizeof(int);
+ *h++=(char)sizeof(size_t);
+ *h++=(char)sizeof(Instruction);
+ *h++=(char)sizeof(lua_Number);
+ *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */
+}
diff --git a/engines/sword25/util/lua/lundump.h b/engines/sword25/util/lua/lundump.h
new file mode 100644
index 0000000000..c80189dbff
--- /dev/null
+++ b/engines/sword25/util/lua/lundump.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "lobject.h"
+#include "lzio.h"
+
+/* load one chunk; from lundump.c */
+LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
+
+/* make header; from lundump.c */
+LUAI_FUNC void luaU_header (char* h);
+
+/* dump one chunk; from ldump.c */
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip);
+
+#ifdef luac_c
+/* print one chunk; from print.c */
+LUAI_FUNC void luaU_print (const Proto* f, int full);
+#endif
+
+/* for header of binary files -- this is Lua 5.1 */
+#define LUAC_VERSION 0x51
+
+/* for header of binary files -- this is the official format */
+#define LUAC_FORMAT 0
+
+/* size of header of binary files */
+#define LUAC_HEADERSIZE 12
+
+#endif
diff --git a/engines/sword25/util/lua/lvm.c b/engines/sword25/util/lua/lvm.c
new file mode 100644
index 0000000000..ee3256ab94
--- /dev/null
+++ b/engines/sword25/util/lua/lvm.c
@@ -0,0 +1,763 @@
+/*
+** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define lvm_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+/* limit for table tag-method chains (to avoid loops) */
+#define MAXTAGLOOP 100
+
+
+const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
+ lua_Number num;
+ if (ttisnumber(obj)) return obj;
+ if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
+ setnvalue(n, num);
+ return n;
+ }
+ else
+ return NULL;
+}
+
+
+int luaV_tostring (lua_State *L, StkId obj) {
+ if (!ttisnumber(obj))
+ return 0;
+ else {
+ char s[LUAI_MAXNUMBER2STR];
+ lua_Number n = nvalue(obj);
+ lua_number2str(s, n);
+ setsvalue2s(L, obj, luaS_new(L, s));
+ return 1;
+ }
+}
+
+
+static void traceexec (lua_State *L, const Instruction *pc) {
+ lu_byte mask = L->hookmask;
+ const Instruction *oldpc = L->savedpc;
+ L->savedpc = pc;
+ if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
+ resethookcount(L);
+ luaD_callhook(L, LUA_HOOKCOUNT, -1);
+ }
+ if (mask & LUA_MASKLINE) {
+ Proto *p = ci_func(L->ci)->l.p;
+ int npc = pcRel(pc, p);
+ int newline = getline(p, npc);
+ /* call linehook when enter a new function, when jump back (loop),
+ or when enter a new line */
+ if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))
+ luaD_callhook(L, LUA_HOOKLINE, newline);
+ }
+}
+
+
+static void callTMres (lua_State *L, StkId res, const TValue *f,
+ const TValue *p1, const TValue *p2) {
+ ptrdiff_t result = savestack(L, res);
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ luaD_checkstack(L, 3);
+ L->top += 3;
+ luaD_call(L, L->top - 3, 1);
+ res = restorestack(L, result);
+ L->top--;
+ setobjs2s(L, res, L->top);
+}
+
+
+
+static void callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3) {
+ setobj2s(L, L->top, f); /* push function */
+ setobj2s(L, L->top+1, p1); /* 1st argument */
+ setobj2s(L, L->top+2, p2); /* 2nd argument */
+ setobj2s(L, L->top+3, p3); /* 3th argument */
+ luaD_checkstack(L, 4);
+ L->top += 4;
+ luaD_call(L, L->top - 4, 0);
+}
+
+
+void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t)) { /* `t' is a table? */
+ Table *h = hvalue(t);
+ const TValue *res = luaH_get(h, key); /* do a primitive get */
+ if (!ttisnil(res) || /* result is no nil? */
+ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
+ setobj2s(L, val, res);
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm)) {
+ callTMres(L, val, tm, t, key);
+ return;
+ }
+ t = tm; /* else repeat with `tm' */
+ }
+ luaG_runerror(L, "loop in gettable");
+}
+
+
+void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
+ int loop;
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm;
+ if (ttistable(t)) { /* `t' is a table? */
+ Table *h = hvalue(t);
+ TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
+ if (!ttisnil(oldval) || /* result is no nil? */
+ (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
+ setobj2t(L, oldval, val);
+ luaC_barriert(L, h, val);
+ return;
+ }
+ /* else will try the tag method */
+ }
+ else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
+ luaG_typeerror(L, t, "index");
+ if (ttisfunction(tm)) {
+ callTM(L, tm, t, key, val);
+ return;
+ }
+ t = tm; /* else repeat with `tm' */
+ }
+ luaG_runerror(L, "loop in settable");
+}
+
+
+static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
+ if (ttisnil(tm))
+ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
+ if (ttisnil(tm)) return 0;
+ callTMres(L, res, tm, p1, p2);
+ return 1;
+}
+
+
+static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,
+ TMS event) {
+ const TValue *tm1 = fasttm(L, mt1, event);
+ const TValue *tm2;
+ if (tm1 == NULL) return NULL; /* no metamethod */
+ if (mt1 == mt2) return tm1; /* same metatables => same metamethods */
+ tm2 = fasttm(L, mt2, event);
+ if (tm2 == NULL) return NULL; /* no metamethod */
+ if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */
+ return tm1;
+ return NULL;
+}
+
+
+static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
+ TMS event) {
+ const TValue *tm1 = luaT_gettmbyobj(L, p1, event);
+ const TValue *tm2;
+ if (ttisnil(tm1)) return -1; /* no metamethod? */
+ tm2 = luaT_gettmbyobj(L, p2, event);
+ if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */
+ return -1;
+ callTMres(L, L->top, tm1, p1, p2);
+ return !l_isfalse(L->top);
+}
+
+
+static int l_strcmp (const TString *ls, const TString *rs) {
+ const char *l = getstr(ls);
+ size_t ll = ls->tsv.len;
+ const char *r = getstr(rs);
+ size_t lr = rs->tsv.len;
+ for (;;) {
+ int temp = strcoll(l, r);
+ if (temp != 0) return temp;
+ else { /* strings are equal up to a `\0' */
+ size_t len = strlen(l); /* index of first `\0' in both strings */
+ if (len == lr) /* r is finished? */
+ return (len == ll) ? 0 : 1;
+ else if (len == ll) /* l is finished? */
+ return -1; /* l is smaller than r (because r is not finished) */
+ /* both strings longer than `len'; go on comparing (after the `\0') */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numlt(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
+ else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
+ return res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+static int lessequal (lua_State *L, const TValue *l, const TValue *r) {
+ int res;
+ if (ttype(l) != ttype(r))
+ return luaG_ordererror(L, l, r);
+ else if (ttisnumber(l))
+ return luai_numle(nvalue(l), nvalue(r));
+ else if (ttisstring(l))
+ return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
+ else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */
+ return res;
+ else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */
+ return !res;
+ return luaG_ordererror(L, l, r);
+}
+
+
+int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
+ const TValue *tm;
+ lua_assert(ttype(t1) == ttype(t2));
+ switch (ttype(t1)) {
+ case LUA_TNIL: return 1;
+ case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
+ case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
+ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
+ case LUA_TUSERDATA: {
+ if (uvalue(t1) == uvalue(t2)) return 1;
+ tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,
+ TM_EQ);
+ break; /* will try TM */
+ }
+ case LUA_TTABLE: {
+ if (hvalue(t1) == hvalue(t2)) return 1;
+ tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ default: return gcvalue(t1) == gcvalue(t2);
+ }
+ if (tm == NULL) return 0; /* no TM? */
+ callTMres(L, L->top, tm, t1, t2); /* call TM */
+ return !l_isfalse(L->top);
+}
+
+
+void luaV_concat (lua_State *L, int total, int last) {
+ do {
+ StkId top = L->base + last + 1;
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
+ if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
+ luaG_concaterror(L, top-2, top-1);
+ } else if (tsvalue(top-1)->len == 0) /* second op is empty? */
+ (void)tostring(L, top - 2); /* result is first op (as string) */
+ else {
+ /* at least two string values; get as many as possible */
+ size_t tl = tsvalue(top-1)->len;
+ char *buffer;
+ int i;
+ /* collect total length */
+ for (n = 1; n < total && tostring(L, top-n-1); n++) {
+ size_t l = tsvalue(top-n-1)->len;
+ if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
+ tl += l;
+ }
+ buffer = luaZ_openspace(L, &G(L)->buff, tl);
+ tl = 0;
+ for (i=n; i>0; i--) { /* concat all strings */
+ size_t l = tsvalue(top-i)->len;
+ memcpy(buffer+tl, svalue(top-i), l);
+ tl += l;
+ }
+ setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
+ }
+ total -= n-1; /* got `n' strings to create 1 new */
+ last -= n-1;
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+static void Arith (lua_State *L, StkId ra, const TValue *rb,
+ const TValue *rc, TMS op) {
+ TValue tempb, tempc;
+ const TValue *b, *c;
+ if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
+ (c = luaV_tonumber(rc, &tempc)) != NULL) {
+ lua_Number nb = nvalue(b), nc = nvalue(c);
+ switch (op) {
+ case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
+ case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
+ case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
+ case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;
+ case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;
+ case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
+ case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
+ default: lua_assert(0); break;
+ }
+ }
+ else if (!call_binTM(L, rb, rc, ra, op))
+ luaG_aritherror(L, rb, rc);
+}
+
+
+
+/*
+** some macros for common tasks in `luaV_execute'
+*/
+
+#define runtime_check(L, c) { if (!(c)) break; }
+
+#define RA(i) (base+GETARG_A(i))
+/* to be used after possible stack reallocation */
+#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
+#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
+#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
+#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
+ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
+#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))
+
+
+#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);}
+
+
+#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
+
+
+#define arith_op(op,tm) { \
+ TValue *rb = RKB(i); \
+ TValue *rc = RKC(i); \
+ if (ttisnumber(rb) && ttisnumber(rc)) { \
+ lua_Number nb = nvalue(rb), nc = nvalue(rc); \
+ setnvalue(ra, op(nb, nc)); \
+ } \
+ else \
+ Protect(Arith(L, ra, rb, rc, tm)); \
+ }
+
+
+
+void luaV_execute (lua_State *L, int nexeccalls) {
+ LClosure *cl;
+ StkId base;
+ TValue *k;
+ const Instruction *pc;
+ reentry: /* entry point */
+ lua_assert(isLua(L->ci));
+ pc = L->savedpc;
+ cl = &clvalue(L->ci->func)->l;
+ base = L->base;
+ k = cl->p->k;
+ /* main loop of interpreter */
+ for (;;) {
+ const Instruction i = *pc++;
+ StkId ra;
+ if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
+ (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
+ traceexec(L, pc);
+ if (L->status == LUA_YIELD) { /* did hook yield? */
+ L->savedpc = pc - 1;
+ return;
+ }
+ base = L->base;
+ }
+ /* warning!! several calls may realloc the stack and invalidate `ra' */
+ ra = RA(i);
+ lua_assert(base == L->base && L->base == L->ci->base);
+ lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
+ lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
+ switch (GET_OPCODE(i)) {
+ case OP_MOVE: {
+ setobjs2s(L, ra, RB(i));
+ continue;
+ }
+ case OP_LOADK: {
+ setobj2s(L, ra, KBx(i));
+ continue;
+ }
+ case OP_LOADBOOL: {
+ setbvalue(ra, GETARG_B(i));
+ if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
+ continue;
+ }
+ case OP_LOADNIL: {
+ TValue *rb = RB(i);
+ do {
+ setnilvalue(rb--);
+ } while (rb >= ra);
+ continue;
+ }
+ case OP_GETUPVAL: {
+ int b = GETARG_B(i);
+ setobj2s(L, ra, cl->upvals[b]->v);
+ continue;
+ }
+ case OP_GETGLOBAL: {
+ TValue g;
+ TValue *rb = KBx(i);
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(rb));
+ Protect(luaV_gettable(L, &g, rb, ra));
+ continue;
+ }
+ case OP_GETTABLE: {
+ Protect(luaV_gettable(L, RB(i), RKC(i), ra));
+ continue;
+ }
+ case OP_SETGLOBAL: {
+ TValue g;
+ sethvalue(L, &g, cl->env);
+ lua_assert(ttisstring(KBx(i)));
+ Protect(luaV_settable(L, &g, KBx(i), ra));
+ continue;
+ }
+ case OP_SETUPVAL: {
+ UpVal *uv = cl->upvals[GETARG_B(i)];
+ setobj(L, uv->v, ra);
+ luaC_barrier(L, uv, ra);
+ continue;
+ }
+ case OP_SETTABLE: {
+ Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
+ continue;
+ }
+ case OP_NEWTABLE: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_SELF: {
+ StkId rb = RB(i);
+ setobjs2s(L, ra+1, rb);
+ Protect(luaV_gettable(L, rb, RKC(i), ra));
+ continue;
+ }
+ case OP_ADD: {
+ arith_op(luai_numadd, TM_ADD);
+ continue;
+ }
+ case OP_SUB: {
+ arith_op(luai_numsub, TM_SUB);
+ continue;
+ }
+ case OP_MUL: {
+ arith_op(luai_nummul, TM_MUL);
+ continue;
+ }
+ case OP_DIV: {
+ arith_op(luai_numdiv, TM_DIV);
+ continue;
+ }
+ case OP_MOD: {
+ arith_op(luai_nummod, TM_MOD);
+ continue;
+ }
+ case OP_POW: {
+ arith_op(luai_numpow, TM_POW);
+ continue;
+ }
+ case OP_UNM: {
+ TValue *rb = RB(i);
+ if (ttisnumber(rb)) {
+ lua_Number nb = nvalue(rb);
+ setnvalue(ra, luai_numunm(nb));
+ }
+ else {
+ Protect(Arith(L, ra, rb, rb, TM_UNM));
+ }
+ continue;
+ }
+ case OP_NOT: {
+ int res = l_isfalse(RB(i)); /* next assignment may change this value */
+ setbvalue(ra, res);
+ continue;
+ }
+ case OP_LEN: {
+ const TValue *rb = RB(i);
+ switch (ttype(rb)) {
+ case LUA_TTABLE: {
+ setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
+ break;
+ }
+ case LUA_TSTRING: {
+ setnvalue(ra, cast_num(tsvalue(rb)->len));
+ break;
+ }
+ default: { /* try metamethod */
+ Protect(
+ if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
+ luaG_typeerror(L, rb, "get length of");
+ )
+ }
+ }
+ continue;
+ }
+ case OP_CONCAT: {
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));
+ setobjs2s(L, RA(i), base+b);
+ continue;
+ }
+ case OP_JMP: {
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_EQ: {
+ TValue *rb = RKB(i);
+ TValue *rc = RKC(i);
+ Protect(
+ if (equalobj(L, rb, rc) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LT: {
+ Protect(
+ if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_LE: {
+ Protect(
+ if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ )
+ pc++;
+ continue;
+ }
+ case OP_TEST: {
+ if (l_isfalse(ra) != GETARG_C(i))
+ dojump(L, pc, GETARG_sBx(*pc));
+ pc++;
+ continue;
+ }
+ case OP_TESTSET: {
+ TValue *rb = RB(i);
+ if (l_isfalse(rb) != GETARG_C(i)) {
+ setobjs2s(L, ra, rb);
+ dojump(L, pc, GETARG_sBx(*pc));
+ }
+ pc++;
+ continue;
+ }
+ case OP_CALL: {
+ int b = GETARG_B(i);
+ int nresults = GETARG_C(i) - 1;
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ switch (luaD_precall(L, ra, nresults)) {
+ case PCRLUA: {
+ nexeccalls++;
+ goto reentry; /* restart luaV_execute over new Lua function */
+ }
+ case PCRC: {
+ /* it was a C function (`precall' called it); adjust results */
+ if (nresults >= 0) L->top = L->ci->top;
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_TAILCALL: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b; /* else previous instruction set top */
+ L->savedpc = pc;
+ lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
+ switch (luaD_precall(L, ra, LUA_MULTRET)) {
+ case PCRLUA: {
+ /* tail call: put new frame in place of previous one */
+ CallInfo *ci = L->ci - 1; /* previous frame */
+ int aux;
+ StkId func = ci->func;
+ StkId pfunc = (ci+1)->func; /* previous function index */
+ if (L->openupval) luaF_close(L, ci->base);
+ L->base = ci->base = ci->func + ((ci+1)->base - pfunc);
+ for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */
+ setobjs2s(L, func+aux, pfunc+aux);
+ ci->top = L->top = func+aux; /* correct top */
+ lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);
+ ci->savedpc = L->savedpc;
+ ci->tailcalls++; /* one more call lost */
+ L->ci--; /* remove new frame */
+ goto reentry;
+ }
+ case PCRC: { /* it was a C function (`precall' called it) */
+ base = L->base;
+ continue;
+ }
+ default: {
+ return; /* yield */
+ }
+ }
+ }
+ case OP_RETURN: {
+ int b = GETARG_B(i);
+ if (b != 0) L->top = ra+b-1;
+ if (L->openupval) luaF_close(L, base);
+ L->savedpc = pc;
+ b = luaD_poscall(L, ra);
+ if (--nexeccalls == 0) /* was previous function running `here'? */
+ return; /* no: return */
+ else { /* yes: continue its execution */
+ if (b) L->top = L->ci->top;
+ lua_assert(isLua(L->ci));
+ lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);
+ goto reentry;
+ }
+ }
+ case OP_FORLOOP: {
+ lua_Number step = nvalue(ra+2);
+ lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */
+ lua_Number limit = nvalue(ra+1);
+ if (luai_numlt(0, step) ? luai_numle(idx, limit)
+ : luai_numle(limit, idx)) {
+ dojump(L, pc, GETARG_sBx(i)); /* jump back */
+ setnvalue(ra, idx); /* update internal index... */
+ setnvalue(ra+3, idx); /* ...and external index */
+ }
+ continue;
+ }
+ case OP_FORPREP: {
+ const TValue *init = ra;
+ const TValue *plimit = ra+1;
+ const TValue *pstep = ra+2;
+ L->savedpc = pc; /* next steps may throw errors */
+ if (!tonumber(init, ra))
+ luaG_runerror(L, LUA_QL("for") " initial value must be a number");
+ else if (!tonumber(plimit, ra+1))
+ luaG_runerror(L, LUA_QL("for") " limit must be a number");
+ else if (!tonumber(pstep, ra+2))
+ luaG_runerror(L, LUA_QL("for") " step must be a number");
+ setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));
+ dojump(L, pc, GETARG_sBx(i));
+ continue;
+ }
+ case OP_TFORLOOP: {
+ StkId cb = ra + 3; /* call base */
+ setobjs2s(L, cb+2, ra+2);
+ setobjs2s(L, cb+1, ra+1);
+ setobjs2s(L, cb, ra);
+ L->top = cb+3; /* func. + 2 args (state and index) */
+ Protect(luaD_call(L, cb, GETARG_C(i)));
+ L->top = L->ci->top;
+ cb = RA(i) + 3; /* previous call may change the stack */
+ if (!ttisnil(cb)) { /* continue loop? */
+ setobjs2s(L, cb-1, cb); /* save control variable */
+ dojump(L, pc, GETARG_sBx(*pc)); /* jump back */
+ }
+ pc++;
+ continue;
+ }
+ case OP_SETLIST: {
+ int n = GETARG_B(i);
+ int c = GETARG_C(i);
+ int last;
+ Table *h;
+ if (n == 0) {
+ n = cast_int(L->top - ra) - 1;
+ L->top = L->ci->top;
+ }
+ if (c == 0) c = cast_int(*pc++);
+ runtime_check(L, ttistable(ra));
+ h = hvalue(ra);
+ last = ((c-1)*LFIELDS_PER_FLUSH) + n;
+ if (last > h->sizearray) /* needs more space? */
+ luaH_resizearray(L, h, last); /* pre-alloc it at once */
+ for (; n > 0; n--) {
+ TValue *val = ra+n;
+ setobj2t(L, luaH_setnum(L, h, last--), val);
+ luaC_barriert(L, h, val);
+ }
+ continue;
+ }
+ case OP_CLOSE: {
+ luaF_close(L, ra);
+ continue;
+ }
+ case OP_CLOSURE: {
+ Proto *p;
+ Closure *ncl;
+ int nup, j;
+ p = cl->p->p[GETARG_Bx(i)];
+ nup = p->nups;
+ ncl = luaF_newLclosure(L, nup, cl->env);
+ ncl->l.p = p;
+ for (j=0; j<nup; j++, pc++) {
+ if (GET_OPCODE(*pc) == OP_GETUPVAL)
+ ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
+ else {
+ lua_assert(GET_OPCODE(*pc) == OP_MOVE);
+ ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
+ }
+ }
+ setclvalue(L, ra, ncl);
+ Protect(luaC_checkGC(L));
+ continue;
+ }
+ case OP_VARARG: {
+ int b = GETARG_B(i) - 1;
+ int j;
+ CallInfo *ci = L->ci;
+ int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;
+ if (b == LUA_MULTRET) {
+ Protect(luaD_checkstack(L, n));
+ ra = RA(i); /* previous call may change the stack */
+ b = n;
+ L->top = ra + n;
+ }
+ for (j = 0; j < b; j++) {
+ if (j < n) {
+ setobjs2s(L, ra + j, ci->base - n + j);
+ }
+ else {
+ setnilvalue(ra + j);
+ }
+ }
+ continue;
+ }
+ }
+ }
+}
+
diff --git a/engines/sword25/util/lua/lvm.h b/engines/sword25/util/lua/lvm.h
new file mode 100644
index 0000000000..bfe4f5678d
--- /dev/null
+++ b/engines/sword25/util/lua/lvm.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))
+
+#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \
+ (((o) = luaV_tonumber(o,n)) != NULL))
+
+#define equalobj(L,o1,o2) \
+ (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))
+
+
+LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);
+LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);
+LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);
+LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,
+ StkId val);
+LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);
+LUAI_FUNC void luaV_concat (lua_State *L, int total, int last);
+
+#endif
diff --git a/engines/sword25/util/lua/lzio.c b/engines/sword25/util/lua/lzio.c
new file mode 100644
index 0000000000..293edd59b0
--- /dev/null
+++ b/engines/sword25/util/lua/lzio.c
@@ -0,0 +1,82 @@
+/*
+** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lzio_c
+#define LUA_CORE
+
+#include "lua.h"
+
+#include "llimits.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+int luaZ_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0) return EOZ;
+ z->n = size - 1;
+ z->p = buff;
+ return char2int(*(z->p++));
+}
+
+
+int luaZ_lookahead (ZIO *z) {
+ if (z->n == 0) {
+ if (luaZ_fill(z) == EOZ)
+ return EOZ;
+ else {
+ z->n++; /* luaZ_fill removed first byte; put back it */
+ z->p--;
+ }
+ }
+ return char2int(*z->p);
+}
+
+
+void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t luaZ_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (luaZ_lookahead(z) == EOZ)
+ return n; /* return number of missing bytes */
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {
+ if (n > buff->buffsize) {
+ if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
+ luaZ_resizebuffer(L, buff, n);
+ }
+ return buff->buffer;
+}
+
+
diff --git a/engines/sword25/util/lua/lzio.h b/engines/sword25/util/lua/lzio.h
new file mode 100644
index 0000000000..51d695d8c1
--- /dev/null
+++ b/engines/sword25/util/lua/lzio.h
@@ -0,0 +1,67 @@
+/*
+** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+#include "lmem.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define char2int(c) cast(int, cast(unsigned char, (c)))
+
+#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z))
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define luaZ_buffer(buff) ((buff)->buffer)
+#define luaZ_sizebuffer(buff) ((buff)->buffsize)
+#define luaZ_bufflen(buff) ((buff)->n)
+
+#define luaZ_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define luaZ_resizebuffer(L, buff, size) \
+ (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
+ (buff)->buffsize = size)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);
+LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */
+LUAI_FUNC int luaZ_lookahead (ZIO *z);
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader;
+ void* data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int luaZ_fill (ZIO *z);
+
+#endif
diff --git a/engines/sword25/util/lua/print.c b/engines/sword25/util/lua/print.c
new file mode 100644
index 0000000000..e240cfc3c6
--- /dev/null
+++ b/engines/sword25/util/lua/print.c
@@ -0,0 +1,227 @@
+/*
+** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $
+** print bytecodes
+** See Copyright Notice in lua.h
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+
+#define luac_c
+#define LUA_CORE
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lundump.h"
+
+#define PrintFunction luaU_print
+
+#define Sizeof(x) ((int)sizeof(x))
+#define VOID(p) ((const void*)(p))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=ts->tsv.len;
+ putchar('"');
+ for (i=0; i<n; i++)
+ {
+ int c=s[i];
+ switch (c)
+ {
+ case '"': printf("\\\""); break;
+ case '\\': printf("\\\\"); break;
+ case '\a': printf("\\a"); break;
+ case '\b': printf("\\b"); break;
+ case '\f': printf("\\f"); break;
+ case '\n': printf("\\n"); break;
+ case '\r': printf("\\r"); break;
+ case '\t': printf("\\t"); break;
+ case '\v': printf("\\v"); break;
+ default: if (isprint((unsigned char)c))
+ putchar(c);
+ else
+ printf("\\%03u",(unsigned char)c);
+ }
+ }
+ putchar('"');
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttype(o))
+ {
+ case LUA_TNIL:
+ printf("nil");
+ break;
+ case LUA_TBOOLEAN:
+ printf(bvalue(o) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ printf(LUA_NUMBER_FMT,nvalue(o));
+ break;
+ case LUA_TSTRING:
+ PrintString(rawtsvalue(o));
+ break;
+ default: /* cannot happen */
+ printf("? type=%d",ttype(o));
+ break;
+ }
+}
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+ Instruction i=code[pc];
+ OpCode o=GET_OPCODE(i);
+ int a=GETARG_A(i);
+ int b=GETARG_B(i);
+ int c=GETARG_C(i);
+ int bx=GETARG_Bx(i);
+ int sbx=GETARG_sBx(i);
+ int line=getline(f,pc);
+ printf("\t%d\t",pc+1);
+ if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+ printf("%-9s\t",luaP_opnames[o]);
+ switch (getOpMode(o))
+ {
+ case iABC:
+ printf("%d",a);
+ if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b);
+ if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c);
+ break;
+ case iABx:
+ if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx);
+ break;
+ case iAsBx:
+ if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx);
+ break;
+ }
+ switch (o)
+ {
+ case OP_LOADK:
+ printf("\t; "); PrintConstant(f,bx);
+ break;
+ case OP_GETUPVAL:
+ case OP_SETUPVAL:
+ printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-");
+ break;
+ case OP_GETGLOBAL:
+ case OP_SETGLOBAL:
+ printf("\t; %s",svalue(&f->k[bx]));
+ break;
+ case OP_GETTABLE:
+ case OP_SELF:
+ if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
+ break;
+ case OP_SETTABLE:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_POW:
+ case OP_EQ:
+ case OP_LT:
+ case OP_LE:
+ if (ISK(b) || ISK(c))
+ {
+ printf("\t; ");
+ if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
+ printf(" ");
+ if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
+ }
+ break;
+ case OP_JMP:
+ case OP_FORLOOP:
+ case OP_FORPREP:
+ printf("\t; to %d",sbx+pc+2);
+ break;
+ case OP_CLOSURE:
+ printf("\t; %p",VOID(f->p[bx]));
+ break;
+ case OP_SETLIST:
+ if (c==0) printf("\t; %d",(int)code[++pc]);
+ else printf("\t; %d",c);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+}
+
+#define SS(x) (x==1)?"":"s"
+#define S(x) x,SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=getstr(f->source);
+ if (*s=='@' || *s=='=')
+ s++;
+ else if (*s==LUA_SIGNATURE[0])
+ s="(bstring)";
+ else
+ s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n",
+ (f->linedefined==0)?"main":"function",s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ f->numparams,f->is_vararg?"+":"",SS(f->numparams),
+ S(f->maxstacksize),S(f->nups));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintConstants(const Proto* f)
+{
+ int i,n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t",i+1);
+ PrintConstant(f,i);
+ printf("\n");
+ }
+}
+
+static void PrintLocals(const Proto* f)
+{
+ int i,n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+}
+
+static void PrintUpvalues(const Proto* f)
+{
+ int i,n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ if (f->upvalues==NULL) return;
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\n",i,getstr(f->upvalues[i]));
+ }
+}
+
+void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full)
+ {
+ PrintConstants(f);
+ PrintLocals(f);
+ PrintUpvalues(f);
+ }
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}
diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG
new file mode 100644
index 0000000000..e31ed26044
--- /dev/null
+++ b/engines/sword25/util/pluto/CHANGELOG
@@ -0,0 +1,38 @@
+$Id$
+
+-- 2.4 --
+* Changed upval unboxing to allow upvals which contain func-housed cycles
+* Added stack checking to all stack-growing functions
+* Serialized debug information for functions
+
+-- 2.3 --
+* Added LUALIB_API declaration for luaopen_pluto
+
+-- 2.2 --
+* Rolled all internal Lua dependencies into the Pluto distribution
+* Made the unit tests depend on dynamically loading Pluto
+
+-- 2.1 --
+* Various fixes to make the GC happy
+* stack size always expanded where necessary
+* fixed some memory leaks
+* GC disabled during unpersist
+* callstack initialized for traversal
+
+This changelog is maintained as of version 2.0alpha1.
+Earlier versions are changelogged on the LuaForge site.
+
+-- 2.0 --
+* Fixed a few format changes to 5.1.3
+* Fixed myriad warnings
+* GCC compliance: not incrementing cast results
+* Fix for self-referring upvals
+* Renamed loading function to work with Lua module system
+* Loading tables with __newindex works
+* unpersist makes buffer copy
+
+-- 2.0alpha1 --
+* Fixed all outstanding 5.0->5.1 conversion issues
+* Made heavier use of size_t in preference to int
+* Fixed GC/Upval issue (thanks to Eric Jacobs)
+
diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT
new file mode 100644
index 0000000000..b3f10ee603
--- /dev/null
+++ b/engines/sword25/util/pluto/FILEFORMAT
@@ -0,0 +1,168 @@
+$Id$
+
+pluto_persist() produces a "hunk" of objects. Here's the file format adhered
+to by the function, and expected by pluto_unpersist().
+
+As a developer, I feel that where file format information is given it is of
+utmost importance that that information precisely and accurately reflects the
+actual operation of the application. Therefore, if you find any discrepancy
+between this and actual operation, please lambast me thoroughly over email.
+
+Pseudo-C is used to express the file format. Padding is assumed to be
+nonexistent. The keyword "one_of" is used to express a concept similar to
+"union", except that its size is the size of the actual datatype chosen. Thus,
+objects which contain, directly or indirectly, a one_of, may vary in size.
+
+
+struct Object {
+ int firstTime; /* Whether this is the first time the object
+ is being referenced */
+ one_of {
+ RealObject o; /* if firstTime == 1 */
+ Reference r; /* if firstTime == 0 */
+ };
+};
+
+struct Reference {
+ int ref; /* The index the object was registered with */
+};
+
+struct RealObject {
+ int type; /* The type of the object */
+ one_of {
+ Boolean b; /* If type == LUA_TBOOLEAN */
+ LightUserData l; /* If type == LUA_TLIGHTUSERDATA */
+ Number n; /* If type == LUA_TNUMBER */
+ String s; /* If type == LUA_TSTRING */
+ Table t; /* If type == LUA_TTABLE */
+ Function f; /* If type == LUA_TFUNCTION */
+ Userdata u; /* If type == LUA_TUSERDATA */
+ Thread th; /* If type == LUA_TTHREAD */
+ Proto p; /* If type == LUA_TPROTO (from lobject.h) */
+ Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */
+ }; /* The actual object */
+};
+
+struct Boolean {
+ int32 bvalue; /* 0 for false, 1 for true */
+};
+
+struct LightUserData {
+ void* luvalue; /* The actual, literal pointer */
+};
+
+struct Number {
+ lua_Number nvalue; /* The actual number */
+};
+
+struct String {
+ int length; /* The length of the string */
+ char str[length]; /* The actual string (not null terminated) */
+};
+
+struct Table {
+ int isspecial; /* 1 if SP is used; 0 otherwise */
+ one_of {
+ Closure c; /* if isspecial == 1; closure to refill the table */
+ LiteralTable t; /* if isspecial == 0; literal table info */
+ };
+};
+
+struct LiteralTable {
+ Object metatable; /* nil for default metatable */
+ Pair p[]; /* key/value pairs */
+ Object nil = nil; /* Nil reference to terminate */
+};
+
+struct Pair {
+ Object key;
+ Object value;
+};
+
+struct Function { /* Actually a closure */
+ lu_byte nups; /* Number of upvalues the function uses */
+ Object proto; /* The proto this function uses */
+ Object upvals[nups]; /* All upvalues */
+ Object fenv; /* The FEnv (nil for the global table)
+};
+
+struct Upval {
+ Object obj; /* The object this upval refers to */
+}
+
+struct Userdata {
+ int isSpecial; /* 1 for special persistence, 0 for literal
+ one_of {
+ LiteralUserdata lu; /* if is_special is 0 */
+ SpecialUserdata su; /* if is_special is 1 */
+ };
+};
+
+struct LiteralUserdata {
+ Object metatable; /* The metatable (nil for default) */
+ int length; /* Size of the data */
+ char data[length]; /* The actual data */
+};
+
+struct SpecialUserdata {
+ int length; /* The size of the data */
+ Object func; /* The closure used to fill the userdata */
+};
+
+struct Thread {
+ int stacksize; /* The size of the stack filled with objects,
+ * including the "nil" that is hidden below
+ * the bottom of the stack visible to C */
+ Object stack[stacksize];/* Indices of all stack values, bottom up */
+ int callinfosize; /* Number of elements in the CallInfo stack */
+ CallInfo callinfostack[callinfosize]; /* The CallInfo stack */
+ int base; /* base = L->base - L->stack; */
+ int top; /* top = L->top - L->stack; */
+ OpenUpval openupvals[]; /* Upvalues to open */
+ Object nil = nil; /* To terminate the open upvalues list */
+};
+
+struct OpenUpval {
+ Object upval; /* The upvalue */
+ int stackpos; /* The stack position to "reopen" it to */
+
+};
+
+struct CallInfo {
+ int base; /* base = ci->base - L->stack; */
+ int top; /* top = ci->top - L->stack; */
+ int pc; /* pc = ci->pc - proto->code; */
+ int state; /* flags used by the CallInfo */
+};
+
+struct Proto {
+ int sizek; /* Number of constants referenced */
+ Object k[sizek]; /* Constants referenced */
+ int sizep; /* Number of inner Protos referenced */
+ Object p[sizep]; /* Inner Protos referenced */
+ int sizecode; /* Number of instructions in code */
+ Instruction code[sizecode]; /* The proto's code */
+ ProtoDebug debuginfo; /* Debug information for the proto */
+ lu_byte nups; /* Number of upvalues used */
+ lu_byte numparams; /* Number of parameters taken */
+ lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */
+ lu_byte maxstacksize; /* Size of stack reserved for the function */
+};
+
+struct ProtoDebug {
+ int sizeupvals; /* Number of upvalue names */
+ Object upvals; /* Upvalue names */
+ int sizelocvars; /* Number of local variable names */
+ LocVar[sizelocvars]; /* Local variable names */
+ Object source; /* The source code */
+ int sizelineinfo; /* Number of opcode-line mappings */
+ int lineinfo[sizelineinfo]; /* opcode-line mappings */
+ int linedefined; /* Start of line range */
+ int lastlinedefined; /* End of line range */
+};
+
+struct LocVar {
+ Object name; /* Name of the local variable */
+ int startpc; /* Point where variable is active */
+ int endpc; /* Point where variable is dead */
+}; \ No newline at end of file
diff --git a/engines/sword25/util/pluto/Makefile b/engines/sword25/util/pluto/Makefile
new file mode 100644
index 0000000000..611ecc83d2
--- /dev/null
+++ b/engines/sword25/util/pluto/Makefile
@@ -0,0 +1,29 @@
+LDLIBS= -lm -ldl -llua
+LDFLAGS = -rdynamic # -L../lua-5.1.3/src
+# CFLAGS= -g3 -Wall -fprofile-arcs -ftest-coverage
+CFLAGS= -g3 -Wall -ansi -pedantic
+
+LIBTOOL=libtool --tag=CC
+
+default: pluto.so pptest puptest
+
+%.lo: %.c
+ $(LIBTOOL) --mode=compile cc $(CFLAGS) -c $<
+
+pluto.so: pluto.lo pdep.lo lzio.lo
+ $(LIBTOOL) --mode=link cc -rpath /usr/local/lib/lua/5.1 -o libpluto.la $^
+ mv .libs/libpluto.so.0.0.0 $@
+
+test: pptest puptest pptest.lua puptest.lua pluto.so
+ ./pptest
+ ./puptest
+
+pptest: pptest.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+puptest: puptest.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+clean:
+ -rm -r *.so *.la *.lo .libs *.a *.o *.bb *.bbg *.da *.gcov pptest puptest test.plh
+
diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README
new file mode 100644
index 0000000000..838fce498b
--- /dev/null
+++ b/engines/sword25/util/pluto/README
@@ -0,0 +1,133 @@
+$Id$
+
+PLUTO - Heavy duty persistence for Lua
+
+Pluto is a library which allows users to write arbitrarily large portions
+of the "Lua universe" into a flat file, and later read them back into the
+same or a different Lua universe. Object references are appropriately
+handled, such that the file contains everything needed to recreate the
+objects in question.
+
+Pluto has the following major features:
+* Can persist any Lua function
+* Can persist threads
+* Works with any Lua chunkreader/chunkwriter
+* Support for "invariant" permanent objects, of all datatypes
+* Can invoke metafunctions for custom persistence of tables and userdata
+
+Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua
+5.0, please use version 1.2 of Pluto.
+
+Starting with version 2.2, Pluto no longer depends on the Lua sources.
+Instead, it subsumes the required headers into its own codebase.
+As a result, it may not work properly with Lua version 5.1.4 or later.
+
+Pluto may have bugs. Users are advised to define lua_assert in
+luaconf.h to something useful when compiling in debug mode, to catch
+assertions by Pluto and Lua.
+
+The Pluto library consists of two public functions.
+
+int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud)
+
+This function recursively persists the Lua object in stack position 2
+and all other objects which are directly or indirectly referenced by
+it, except those referenced in the permanent object table. The data
+is written using the chunk-writer given, and that writer is passed
+the arbitrary pointer value ud.
+
+The Lua stack must contain exactly and only these two items, in order:
+
+1. A table of permanent objects, that should not be persisted. For each
+permanent object, the object itself should be the key, and a unique
+object of any type should be the value. Likely candidates for this table
+include Lua functions (including those in the Lua libraries) that are
+loaded at load-time. It must include all non-persistable objects that
+are referenced by the object to be persisted. The table is not modified
+by the function. Objects in this table are considered "opaque" and are
+not examined or descended into. Objects should not appear in the table
+multiple times; the result of doing this is undefined (though probably
+harmless). NOTE: If you are planning to persist threads, keep in mind
+that all yielded threads have coroutine.yield on the tops of their
+stacks. Since it's a C function, it should be put here. For complex
+permanents, it may be a good idea to use the __index meta-function of
+the permanents table to "search" for permanents.
+
+2. The single object to be persisted. In many cases, this will be the
+global table. For more flexibility, however, it may be something like a
+table built for the occasion, with various values to keep track of. The
+object may not be nil.
+
+
+int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud)
+
+This function loads in a Lua object and places it on top of the stack. All
+objects directly or indirectly referenced by it are also loaded.
+
+The Lua stack must contain, as its top value, a table of permanent
+objects. This table should be like the permanent object table used when
+persisting, but with the key and value of each pair reversed. These
+objects are used as substitutes for those referenced in their positions
+when persisting, and under most circumstances should be identical objects
+to those referenced in the permanents table used for persisting. It's
+okay for multiple keys to refer to the same object.
+
+
+RUNNING PLUTO FROM LUA:
+It is also possible to invoke pluto from a Lua script. The C function
+pluto_open() will register pluto.persist and pluto.unpersist, lua functions
+which operate on strings. The first takes a permanents table and a root
+object, and returns a string; the second takes a permanents table and a
+string, and returns the root object.
+
+An error will be raised if pluto.persist is called from a thread which is
+itself referenced by the root object.
+
+SPECIAL PERSISTENCE:
+Tables and userdata have special persistence semantics. These semantics are
+keyed to the value of the object's metatable's __persist member, if any. This
+member may be any of the following four values:
+1. Boolean "true": The table or userdata is persisted literally; tables are
+persisted member-by-member, and userdata are written out as literal data.
+2. Boolean "false": An error is returned, indicating that the object cannot
+be persisted.
+3. A function: This function should take one argument, the object in question,
+and return one result, a closure. This "fixup closure", in turn, will be
+persisted, and during unpersistence will be called. The closure will be
+responsible for recreating the object with the appropriate data, based on
+its upvalues.
+4. Nil, or no metatable. In the case of tables, the table is literally
+persisted. In the case of userdata, an error is returned.
+
+Here's an example of special persistence for a simple 3d vector object:
+
+vec = { x = 2, y = 1, z = 4 }
+setmetatable(vec, { __persist = function(oldtbl)
+ local x = oldtbl.x
+ local y = oldtbl.y
+ local z = oldtbl.z
+ local mt = getmetatable(oldtbl)
+ return function()
+ newtbl = {}
+ newtbl.x = x
+ newtbl.y = y
+ newtbl.z = z
+ setmetatable(newtbl, mt)
+ return newtbl
+ end
+end })
+
+Note how x, y, z, and the mt are explicitly pulled out of the table. It is
+important that the fixup closure returned not reference the original table
+directly, as that table would again be persisted as an upvalue, leading to an
+infinite loop. Also note that the object's metatable is NOT automatically
+persisted; it is necessary for the fixup closure to reset it, if it wants.
+
+LIMITATIONS/TODO:
+* Light userdata are persisted literally, as their pointer values. This
+may or may not be what you want.
+* Closures of C functions may not be persisted. Once it becomes possible
+to specify a C function "proto" as a permanent object, this restriction
+will be relaxed.
+
+BUGS: None known. Emphasis on the 'known'.
diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS
new file mode 100644
index 0000000000..fea3595dbf
--- /dev/null
+++ b/engines/sword25/util/pluto/THANKS
@@ -0,0 +1,10 @@
+Pluto is surprisingly robust and useful. This would not be the case without
+the hard work and helpfulness of the following people, mentioned in no
+particular order:
+
+Ivko Stanilov
+Goran Adrinek
+Eric Jacobs
+Anolan Milanes
+Malte Thiesen
+
diff --git a/engines/sword25/util/pluto/pdep.c b/engines/sword25/util/pluto/pdep.c
new file mode 100644
index 0000000000..a32c43b42d
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep.c
@@ -0,0 +1,112 @@
+/* This file is derived from the Lua source code. Please see lua.h for
+the copyright statement.
+*/
+
+#include "pdep/pdep.h"
+
+#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;}
+
+void pdep_pushobject (lua_State *L, const TValue *o) {
+ setobj2s(L, L->top, o);
+ api_incr_top(L);
+}
+
+void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ block = (*g->frealloc)(g->ud, block, osize, nsize);
+ lua_assert((nsize == 0) == (block == NULL));
+ g->totalbytes = (g->totalbytes - osize) + nsize;
+ return block;
+}
+
+void pdep_link (lua_State *L, GCObject *o, lu_byte tt) {
+ global_State *g = G(L);
+ o->gch.next = g->rootgc;
+ g->rootgc = o;
+ o->gch.marked = luaC_white(g);
+ o->gch.tt = tt;
+}
+
+Proto *pdep_newproto (lua_State *L) {
+ Proto *f = pdep_new(L, Proto);
+ pdep_link(L, obj2gco(f), LUA_TPROTO);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->sizelineinfo = 0;
+ f->sizeupvalues = 0;
+ f->nups = 0;
+ f->upvalues = NULL;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->lineinfo = NULL;
+ f->sizelocvars = 0;
+ f->locvars = NULL;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) {
+ Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems)));
+ pdep_link(L, obj2gco(c), LUA_TFUNCTION);
+ c->l.isC = 0;
+ c->l.env = e;
+ c->l.nupvalues = cast_byte(nelems);
+ while (nelems--) c->l.upvals[nelems] = NULL;
+ return c;
+}
+
+static void correctstack (lua_State *L, TValue *oldstack) {
+ CallInfo *ci;
+ GCObject *up;
+ L->top = (L->top - oldstack) + L->stack;
+ for (up = L->openupval; up != NULL; up = up->gch.next)
+ gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
+ for (ci = L->base_ci; ci <= L->ci; ci++) {
+ ci->top = (ci->top - oldstack) + L->stack;
+ ci->base = (ci->base - oldstack) + L->stack;
+ ci->func = (ci->func - oldstack) + L->stack;
+ }
+ L->base = (L->base - oldstack) + L->stack;
+}
+
+
+void pdep_reallocstack (lua_State *L, int newsize) {
+ TValue *oldstack = L->stack;
+ int realsize = newsize + 1 + EXTRA_STACK;
+ lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
+ pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
+ L->stacksize = realsize;
+ L->stack_last = L->stack+newsize;
+ correctstack(L, oldstack);
+}
+
+void pdep_growstack (lua_State *L, int n) {
+ if (n <= L->stacksize) /* double size is enough? */
+ pdep_reallocstack(L, 2*L->stacksize);
+ else
+ pdep_reallocstack(L, L->stacksize + n);
+}
+
+void pdep_reallocCI (lua_State *L, int newsize) {
+ CallInfo *oldci = L->base_ci;
+ pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
+ L->size_ci = newsize;
+ L->ci = (L->ci - oldci) + L->base_ci;
+ L->end_ci = L->base_ci + L->size_ci - 1;
+}
+
+TString *pdep_newlstr (lua_State *L, const char *str, size_t l) {
+ TString *res;
+ lua_pushlstring(L, str, l);
+ res = rawtsvalue(L->top-1);
+ lua_pop(L, 1);
+ return res;
+}
diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README
new file mode 100644
index 0000000000..3592754da0
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/README
@@ -0,0 +1,5 @@
+These files are directly copied from the Lua distribution, with the
+exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed.
+
+As such, unlike the rest of Pluto, they are released under the
+same terms as Lua. See "lua.h" for the copyright notice.
diff --git a/engines/sword25/util/pluto/pdep/lauxlib.h b/engines/sword25/util/pluto/pdep/lauxlib.h
new file mode 100644
index 0000000000..34258235db
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lauxlib.h
@@ -0,0 +1,174 @@
+/*
+** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "lua.h"
+
+
+#if defined(LUA_COMPAT_GETN)
+LUALIB_API int (luaL_getn) (lua_State *L, int t);
+LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
+#else
+#define luaL_getn(L,i) ((int)lua_objlen(L, i))
+#define luaL_setn(L,i,j) ((void)0) /* no op! */
+#endif
+
+#if defined(LUA_COMPAT_OPENLIB)
+#define luaI_openlib luaL_openlib
+#endif
+
+
+/* extra error code for `luaL_load' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+
+LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
+ const luaL_Reg *l, int nup);
+LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
+ const luaL_Reg *l);
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
+LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
+LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
+ const char *name);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
+ const char *r);
+
+LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
+ const char *fname, int szhint);
+
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define luaL_argcheck(L, cond,numarg,extramsg) \
+ ((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+
+
+typedef struct luaL_Buffer {
+ char *p; /* current position in buffer */
+ int lvl; /* number of strings in the stack (level) */
+ lua_State *L;
+ char buffer[LUAL_BUFFERSIZE];
+} luaL_Buffer;
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
+ (*(B)->p++ = (char)(c)))
+
+/* compatibility only */
+#define luaL_putchar(B,c) luaL_addchar(B,c)
+
+#define luaL_addsize(B,n) ((B)->p += (n))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+
+
+/* }====================================================== */
+
+
+/* compatibility with ref system */
+
+/* pre-defined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
+ (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
+
+#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
+
+#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
+
+
+#define luaL_reg luaL_Reg
+
+#endif
+
+
diff --git a/engines/sword25/util/pluto/pdep/ldo.h b/engines/sword25/util/pluto/pdep/ldo.h
new file mode 100644
index 0000000000..98fddac59f
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ldo.h
@@ -0,0 +1,57 @@
+/*
+** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+#define luaD_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ luaD_growstack(L, n); \
+ else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
+
+
+#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
+
+#define savestack(L,p) ((char *)(p) - (char *)L->stack)
+#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
+
+#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
+#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
+
+
+/* results from luaD_precall */
+#define PCRLUA 0 /* initiated a call to a Lua function */
+#define PCRC 1 /* did a call to a C function */
+#define PCRYIELD 2 /* C funtion yielded */
+
+
+/* type of protected functions, to be ran by `runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
+LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
+LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
+LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
+LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
+LUAI_FUNC void luaD_growstack (lua_State *L, int n);
+
+LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lfunc.h b/engines/sword25/util/pluto/pdep/lfunc.h
new file mode 100644
index 0000000000..a68cf5151c
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lfunc.h
@@ -0,0 +1,34 @@
+/*
+** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
+ cast(int, sizeof(TValue)*((n)-1)))
+
+#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
+ cast(int, sizeof(TValue *)*((n)-1)))
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
+LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_close (lua_State *L, StkId level);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
+LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lgc.h b/engines/sword25/util/pluto/pdep/lgc.h
new file mode 100644
index 0000000000..5a8dc605b3
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lgc.h
@@ -0,0 +1,110 @@
+/*
+** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpause 0
+#define GCSpropagate 1
+#define GCSsweepstring 2
+#define GCSsweep 3
+#define GCSfinalize 4
+
+
+/*
+** some userful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
+#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
+#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
+
+
+
+/*
+** Layout for bit use in `marked' field:
+** bit 0 - object is white (type 0)
+** bit 1 - object is white (type 1)
+** bit 2 - object is black
+** bit 3 - for userdata: has been finalized
+** bit 3 - for tables: has weak keys
+** bit 4 - for tables: has weak values
+** bit 5 - object is fixed (should not be collected)
+** bit 6 - object is "super" fixed (only the main thread)
+*/
+
+
+#define WHITE0BIT 0
+#define WHITE1BIT 1
+#define BLACKBIT 2
+#define FINALIZEDBIT 3
+#define KEYWEAKBIT 3
+#define VALUEWEAKBIT 4
+#define FIXEDBIT 5
+#define SFIXEDBIT 6
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
+#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
+#define isgray(x) (!isblack(x) && !iswhite(x))
+
+#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
+#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
+
+#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
+#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
+
+
+#define luaC_checkGC(L) { \
+ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
+ if (G(L)->totalbytes >= G(L)->GCthreshold) \
+ luaC_step(L); }
+
+
+#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
+
+#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
+ luaC_barrierback(L,t); }
+
+#define luaC_objbarrier(L,p,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
+ luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
+
+#define luaC_objbarriert(L,t,o) \
+ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
+
+LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
+LUAI_FUNC void luaC_callGCTM (lua_State *L);
+LUAI_FUNC void luaC_freeall (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_fullgc (lua_State *L);
+LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
+LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
+LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/llimits.h b/engines/sword25/util/pluto/pdep/llimits.h
new file mode 100644
index 0000000000..ca8dcb7224
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/llimits.h
@@ -0,0 +1,128 @@
+/*
+** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
+** Limits, basic types, and some other `installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+typedef LUAI_UINT32 lu_int32;
+
+typedef LUAI_UMEM lu_mem;
+
+typedef LUAI_MEM l_mem;
+
+
+
+/* chars used as small naturals (so that `char' is reserved for characters) */
+typedef unsigned char lu_byte;
+
+
+#define MAX_SIZET ((size_t)(~(size_t)0)-2)
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
+
+
+#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
+
+/*
+** conversion of pointer to integer
+** this is for hashing only; there is no problem if the integer
+** cannot hold the whole pointer value
+*/
+#define IntPoint(p) ((unsigned int)(lu_mem)(p))
+
+
+
+/* type to ensure maximum alignment */
+typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
+
+
+/* result of a `usual argument conversion' over lua_Number */
+typedef LUAI_UACNUMBER l_uacNumber;
+
+
+/* internal assertions for in-house debugging */
+#ifdef lua_assert
+
+#define check_exp(c,e) (lua_assert(c), (e))
+#define api_check(l,e) lua_assert(e)
+
+#else
+
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define api_check luai_apicheck
+
+#endif
+
+
+#ifndef UNUSED
+#define UNUSED(x) ((void)(x)) /* to avoid warnings */
+#endif
+
+
+#ifndef cast
+#define cast(t, exp) ((t)(exp))
+#endif
+
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+
+
+
+/*
+** type for virtual-machine instructions
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+typedef lu_int32 Instruction;
+
+
+
+/* maximum stack for a Lua function */
+#define MAXSTACK 250
+
+
+
+/* minimum size for the string table (must be power of 2) */
+#ifndef MINSTRTABSIZE
+#define MINSTRTABSIZE 32
+#endif
+
+
+/* minimum size for string buffer */
+#ifndef LUA_MINBUFFER
+#define LUA_MINBUFFER 32
+#endif
+
+
+#ifndef lua_lock
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+#ifndef luai_threadyield
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#ifndef HARDSTACKTESTS
+#define condhardstacktests(x) ((void)0)
+#else
+#define condhardstacktests(x) x
+#endif
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lobject.h b/engines/sword25/util/pluto/pdep/lobject.h
new file mode 100644
index 0000000000..e7199dfc68
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lobject.h
@@ -0,0 +1,381 @@
+/*
+** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/* tags for values visible from Lua */
+#define LAST_TAG LUA_TTHREAD
+
+#define NUM_TAGS (LAST_TAG+1)
+
+
+/*
+** Extra tags for non-values
+*/
+#define LUA_TPROTO (LAST_TAG+1)
+#define LUA_TUPVAL (LAST_TAG+2)
+#define LUA_TDEADKEY (LAST_TAG+3)
+
+
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
+
+
+/*
+** Common header in struct form
+*/
+typedef struct GCheader {
+ CommonHeader;
+} GCheader;
+
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union {
+ GCObject *gc;
+ void *p;
+ lua_Number n;
+ int b;
+} Value;
+
+
+/*
+** Tagged Values
+*/
+
+#define TValuefields Value value; int tt
+
+typedef struct lua_TValue {
+ TValuefields;
+} TValue;
+
+
+/* Macros to test type */
+#define ttisnil(o) (ttype(o) == LUA_TNIL)
+#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
+#define ttisstring(o) (ttype(o) == LUA_TSTRING)
+#define ttistable(o) (ttype(o) == LUA_TTABLE)
+#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
+#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
+#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
+#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
+
+/* Macros to access values */
+#define ttype(o) ((o)->tt)
+#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
+#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
+#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
+#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
+#define tsvalue(o) (&rawtsvalue(o)->tsv)
+#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
+#define uvalue(o) (&rawuvalue(o)->uv)
+#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
+#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
+#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
+
+#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
+
+/*
+** for internal debug only
+*/
+#define checkconsistency(obj) \
+ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
+
+#define checkliveness(g,obj) \
+ lua_assert(!iscollectable(obj) || \
+ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
+
+
+/* Macros to set values */
+#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
+
+#define setnvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
+
+#define setpvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
+
+#define setbvalue(obj,x) \
+ { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
+
+#define setsvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
+ checkliveness(G(L),i_o); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
+ checkliveness(G(L),i_o); }
+
+#define setthvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
+ checkliveness(G(L),i_o); }
+
+#define setclvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
+ checkliveness(G(L),i_o); }
+
+#define sethvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
+ checkliveness(G(L),i_o); }
+
+#define setptvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
+ checkliveness(G(L),i_o); }
+
+
+
+
+#define setobj(L,obj1,obj2) \
+ { const TValue *o2=(obj2); TValue *o1=(obj1); \
+ o1->value = o2->value; o1->tt=o2->tt; \
+ checkliveness(G(L),o1); }
+
+
+/*
+** different types of sets, according to destination
+*/
+
+/* from stack to (same) stack */
+#define setobjs2s setobj
+/* to stack (not from same stack) */
+#define setobj2s setobj
+#define setsvalue2s setsvalue
+#define sethvalue2s sethvalue
+#define setptvalue2s setptvalue
+/* from table to same table */
+#define setobjt2t setobj
+/* to table */
+#define setobj2t setobj
+/* to new object */
+#define setobj2n setobj
+#define setsvalue2n setsvalue
+
+#define setttype(obj, tt) (ttype(obj) = (tt))
+
+
+#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
+
+
+
+typedef TValue *StkId; /* index to stack elements */
+
+
+/*
+** String headers for string table
+*/
+typedef union TString {
+ L_Umaxalign dummy; /* ensures maximum alignment for strings */
+ struct {
+ CommonHeader;
+ lu_byte reserved;
+ unsigned int hash;
+ size_t len;
+ } tsv;
+} TString;
+
+
+#define getstr(ts) cast(const char *, (ts) + 1)
+#define svalue(o) getstr(tsvalue(o))
+
+
+
+typedef union Udata {
+ L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
+ struct {
+ CommonHeader;
+ struct Table *metatable;
+ struct Table *env;
+ size_t len;
+ } uv;
+} Udata;
+
+
+
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ TValue *k; /* constants used by the function */
+ Instruction *code;
+ struct Proto **p; /* functions defined inside the function */
+ int *lineinfo; /* map from opcodes to source lines */
+ struct LocVar *locvars; /* information about local variables */
+ TString **upvalues; /* upvalue names */
+ TString *source;
+ int sizeupvalues;
+ int sizek; /* size of `k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of `p' */
+ int sizelocvars;
+ int linedefined;
+ int lastlinedefined;
+ GCObject *gclist;
+ lu_byte nups; /* number of upvalues */
+ lu_byte numparams;
+ lu_byte is_vararg;
+ lu_byte maxstacksize;
+} Proto;
+
+
+/* masks for new-style vararg */
+#define VARARG_HASARG 1
+#define VARARG_ISVARARG 2
+#define VARARG_NEEDSARG 4
+
+
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+
+/*
+** Upvalues
+*/
+
+typedef struct UpVal {
+ CommonHeader;
+ TValue *v; /* points to stack or to its own value */
+ union {
+ TValue value; /* the value (when closed) */
+ struct { /* double linked list (when open) */
+ struct UpVal *prev;
+ struct UpVal *next;
+ } l;
+ } u;
+} UpVal;
+
+
+/*
+** Closures
+*/
+
+#define ClosureHeader \
+ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
+ struct Table *env
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1];
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1];
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
+#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
+
+
+/*
+** Tables
+*/
+
+typedef union TKey {
+ struct {
+ TValuefields;
+ struct Node *next; /* for chaining */
+ } nk;
+ TValue tvk;
+} TKey;
+
+
+typedef struct Node {
+ TValue i_val;
+ TKey i_key;
+} Node;
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of `node' array */
+ struct Table *metatable;
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ GCObject *gclist;
+ int sizearray; /* size of `array' array */
+} Table;
+
+
+
+/*
+** `module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+#define luaO_nilobject (&luaO_nilobject_)
+
+LUAI_DATA const TValue luaO_nilobject_;
+
+#define ceillog2(x) (luaO_log2((x)-1) + 1)
+
+LUAI_FUNC int luaO_log2 (unsigned int x);
+LUAI_FUNC int luaO_int2fb (unsigned int x);
+LUAI_FUNC int luaO_fb2int (int x);
+LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
+
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lopcodes.h b/engines/sword25/util/pluto/pdep/lopcodes.h
new file mode 100644
index 0000000000..41224d6ee1
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lopcodes.h
@@ -0,0 +1,268 @@
+/*
+** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned numbers.
+ All instructions have an opcode in the first 6 bits.
+ Instructions can have the following fields:
+ `A' : 8 bits
+ `B' : 9 bits
+ `C' : 9 bits
+ `Bx' : 18 bits (`B' and `C' together)
+ `sBx' : signed Bx
+
+ A signed argument is represented in excess K; that is, the number
+ value is the unsigned value minus K. K is exactly the maximum value
+ for that argument (so that -max is represented by 0, and +max is
+ represented by 2*max), which is half the maximum for the corresponding
+ unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 9
+#define SIZE_B 9
+#define SIZE_Bx (SIZE_C + SIZE_B)
+#define SIZE_A 8
+
+#define SIZE_OP 6
+
+#define POS_OP 0
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_C (POS_A + SIZE_A)
+#define POS_B (POS_C + SIZE_C)
+#define POS_Bx POS_C
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) int to manipulate most arguments,
+** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
+*/
+#if SIZE_Bx < LUAI_BITSINT-1
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
+#else
+#define MAXARG_Bx MAX_INT
+#define MAXARG_sBx MAX_INT
+#endif
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+
+
+/* creates a mask with `n' 1 bits at position `p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
+
+/* creates a mask with `n' 0 bits at position `p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
+#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
+ ((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
+
+#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
+#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
+ ((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
+
+#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
+#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
+ ((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
+
+#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
+#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
+ ((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
+
+#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
+
+
+#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+
+/*
+** Macros to operate RK indices
+*/
+
+/* this bit 1 means constant (0 means register) */
+#define BITRK (1 << (SIZE_B - 1))
+
+/* test whether value is a constant */
+#define ISK(x) ((x) & BITRK)
+
+/* gets the index of the constant */
+#define INDEXK(r) ((int)(r) & ~BITRK)
+
+#define MAXINDEXRK (BITRK - 1)
+
+/* code a constant index as a RK value */
+#define RKASK(x) ((x) | BITRK)
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R(x) - register
+** Kst(x) - constant (in constant table)
+** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
+*/
+
+
+/*
+** grep "ORDER OP" if you change these enums
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R(A) := R(B) */
+OP_LOADK,/* A Bx R(A) := Kst(Bx) */
+OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
+OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
+OP_GETUPVAL,/* A B R(A) := UpValue[B] */
+
+OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
+OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
+
+OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
+OP_SETUPVAL,/* A B UpValue[B] := R(A) */
+OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
+
+OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
+
+OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
+
+OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
+OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
+OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
+OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
+OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
+OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
+OP_UNM,/* A B R(A) := -R(B) */
+OP_NOT,/* A B R(A) := not R(B) */
+OP_LEN,/* A B R(A) := length of R(B) */
+
+OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
+
+OP_JMP,/* sBx pc+=sBx */
+
+OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
+OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
+OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
+
+OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
+OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
+
+OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
+OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
+
+OP_FORLOOP,/* A sBx R(A)+=R(A+2);
+ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
+OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
+
+OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
+ if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
+OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
+
+OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
+OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
+
+OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
+} OpCode;
+
+
+#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+ (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
+ and can be 0: OP_CALL then sets `top' to last_result+1, so
+ next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
+
+ (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to `top'
+
+ (*) In OP_SETLIST, if (B == 0) then B = `top';
+ if (C == 0) then next `instruction' is real C
+
+ (*) For comparisons, A specifies what condition the test should accept
+ (true or false).
+
+ (*) All `skips' (pc++) assume that next instruction is a jump
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-1: op mode
+** bits 2-3: C arg mode
+** bits 4-5: B arg mode
+** bit 6: instruction set register A
+** bit 7: operator is a test
+*/
+
+enum OpArgMask {
+ OpArgN, /* argument is not used */
+ OpArgU, /* argument is used */
+ OpArgR, /* argument is a register or a jump offset */
+ OpArgK /* argument is a constant or register/constant */
+};
+
+LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
+#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
+#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
+
+
+LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lstate.h b/engines/sword25/util/pluto/pdep/lstate.h
new file mode 100644
index 0000000000..3bc575b6bc
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstate.h
@@ -0,0 +1,169 @@
+/*
+** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/* table of globals */
+#define gt(L) (&L->l_gt)
+
+/* registry */
+#define registry(L) (&G(L)->l_registry)
+
+
+/* extra stack space to handle TM calls and some other extras */
+#define EXTRA_STACK 5
+
+
+#define BASIC_CI_SIZE 8
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+
+
+typedef struct stringtable {
+ GCObject **hash;
+ lu_int32 nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** informations about a call
+*/
+typedef struct CallInfo {
+ StkId base; /* base for this function */
+ StkId func; /* function index in the stack */
+ StkId top; /* top for this function */
+ const Instruction *savedpc;
+ int nresults; /* expected number of results from this function */
+ int tailcalls; /* number of tail calls lost under this entry */
+} CallInfo;
+
+
+
+#define curr_func(L) (clvalue(L->ci->func))
+#define ci_func(ci) (clvalue((ci)->func))
+#define f_isLua(ci) (!ci_func(ci)->c.isC)
+#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
+
+
+/*
+** `global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ stringtable strt; /* hash table for strings */
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to `frealloc' */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ int sweepstrgc; /* position of sweep in `strt' */
+ GCObject *rootgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* position of sweep in `rootgc' */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of weak tables (to be cleared) */
+ GCObject *tmudata; /* last element of list of userdata to be GC */
+ Mbuffer buff; /* temporary buffer for string concatentation */
+ lu_mem GCthreshold;
+ lu_mem totalbytes; /* number of bytes currently allocated */
+ lu_mem estimate; /* an estimate of number of bytes actually in use */
+ lu_mem gcdept; /* how much GC is `behind schedule' */
+ int gcpause; /* size of pause between successive GCs */
+ int gcstepmul; /* GC `granularity' */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ TValue l_registry;
+ struct lua_State *mainthread;
+ UpVal uvhead; /* head of double-linked list of all open upvalues */
+ struct Table *mt[NUM_TAGS]; /* metatables for basic types */
+ TString *tmname[TM_N]; /* array with tag-method names */
+} global_State;
+
+
+/*
+** `per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ StkId top; /* first free slot in the stack */
+ StkId base; /* base of current function */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ const Instruction *savedpc; /* `savedpc' of current function */
+ StkId stack_last; /* last free slot in the stack */
+ StkId stack; /* stack base */
+ CallInfo *end_ci; /* points after end of ci array*/
+ CallInfo *base_ci; /* array of CallInfo's */
+ int stacksize;
+ int size_ci; /* size of array `base_ci' */
+ unsigned short nCcalls; /* number of nested C calls */
+ unsigned short baseCcalls; /* nested C calls when resuming coroutine */
+ lu_byte hookmask;
+ lu_byte allowhook;
+ int basehookcount;
+ int hookcount;
+ lua_Hook hook;
+ TValue l_gt; /* table of globals */
+ TValue env; /* temporary place for environments */
+ GCObject *openupval; /* list of open upvalues in this stack */
+ GCObject *gclist;
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+};
+
+
+#define G(L) (L->l_G)
+
+
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+ GCheader gch;
+ union TString ts;
+ union Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct UpVal uv;
+ struct lua_State th; /* thread */
+};
+
+
+/* macros to convert a GCObject into a specific value */
+#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
+#define gco2ts(o) (&rawgco2ts(o)->tsv)
+#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
+#define gco2u(o) (&rawgco2u(o)->uv)
+#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
+#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
+#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
+#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define ngcotouv(o) \
+ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
+#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
+
+/* macro to convert any Lua object into a GCObject */
+#define obj2gco(v) (cast(GCObject *, (v)))
+
+
+LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+
+#endif
+
diff --git a/engines/sword25/util/pluto/pdep/lstring.h b/engines/sword25/util/pluto/pdep/lstring.h
new file mode 100644
index 0000000000..73a2ff8b38
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lstring.h
@@ -0,0 +1,31 @@
+/*
+** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
+
+#define sizeudata(u) (sizeof(union Udata)+(u)->len)
+
+#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT)
+
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/ltm.h b/engines/sword25/util/pluto/pdep/ltm.h
new file mode 100644
index 0000000000..64343b781b
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/ltm.h
@@ -0,0 +1,54 @@
+/*
+** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_EQ, /* last tag method with `fast' access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_DIV,
+ TM_MOD,
+ TM_POW,
+ TM_UNM,
+ TM_LEN,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+LUAI_DATA const char *const luaT_typenames[];
+
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lua.h b/engines/sword25/util/pluto/pdep/lua.h
new file mode 100644
index 0000000000..0f3f28fce5
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lua.h
@@ -0,0 +1,388 @@
+/*
+** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $
+** Lua - An Extensible Extension Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "sword25/util/lua/luaconf.h"
+
+
+#define LUA_VERSION "Lua 5.1"
+#define LUA_RELEASE "Lua 5.1.3"
+#define LUA_VERSION_NUM 501
+#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
+
+
+/* mark for precompiled code (`<esc>Lua') */
+#define LUA_SIGNATURE "\033Lua"
+
+/* option for multiple returns in `lua_pcall' and `lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** pseudo-indices
+*/
+#define LUA_REGISTRYINDEX (-10000)
+#define LUA_ENVIRONINDEX (-10001)
+#define LUA_GLOBALSINDEX (-10002)
+#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
+
+
+/* thread status; 0 is OK */
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+typedef int (*lua_CFunction) (lua_State *L);
+
+
+/*
+** functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
+
+
+/*
+** prototype for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_remove) (lua_State *L, int idx);
+LUA_API void (lua_insert) (lua_State *L, int idx);
+LUA_API void (lua_replace) (lua_State *L, int idx);
+LUA_API int (lua_checkstack) (lua_State *L, int sz);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
+
+LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
+LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API size_t (lua_objlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
+LUA_API void (lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API void (lua_gettable) (lua_State *L, int idx);
+LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawget) (lua_State *L, int idx);
+LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API void (lua_getfenv) (lua_State *L, int idx);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setfenv) (lua_State *L, int idx);
+
+
+/*
+** `load' and `call' functions (load and run Lua code)
+*/
+LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
+LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
+LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yield) (lua_State *L, int nresults);
+LUA_API int (lua_resume) (lua_State *L, int narg);
+LUA_API int (lua_status) (lua_State *L);
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+
+LUA_API int (lua_gc) (lua_State *L, int what, int data);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
+
+
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_strlen(L,i) lua_objlen(L, (i))
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) \
+ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
+
+#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
+#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+
+/*
+** compatibility macros and functions
+*/
+
+#define lua_open() luaL_newstate()
+
+#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
+
+#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
+
+#define lua_Chunkreader lua_Reader
+#define lua_Chunkwriter lua_Writer
+
+
+/* hack */
+LUA_API void lua_setlevel (lua_State *from, lua_State *to);
+
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILRET 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+typedef struct lua_Debug lua_Debug; /* activation record */
+
+
+/* Functions to be called by the debuger in specific events */
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
+
+LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook lua_gethook (lua_State *L);
+LUA_API int lua_gethookmask (lua_State *L);
+LUA_API int lua_gethookcount (lua_State *L);
+
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) `global', `local', `field', `method' */
+ const char *what; /* (S) `Lua', `C', `main', `tail' */
+ const char *source; /* (S) */
+ int currentline; /* (l) */
+ int nups; /* (u) number of upvalues */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ int i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2008 Lua.org, PUC-Rio. 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, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* 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.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h
new file mode 100644
index 0000000000..4e654a52c9
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/lzio.h
@@ -0,0 +1,65 @@
+/*
+** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define char2int(c) cast(int, cast(unsigned char, (c)))
+
+#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z))
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define pdep_buffer(buff) ((buff)->buffer)
+#define pdep_sizebuffer(buff) ((buff)->buffsize)
+#define pdep_bufflen(buff) ((buff)->n)
+
+#define pdep_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define pdep_resizebuffer(L, buff, size) \
+ (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \
+ (buff)->buffsize = size)
+
+#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n);
+LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */
+LUAI_FUNC int pdep_lookahead (ZIO *z);
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader;
+ void* data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int pdep_fill (ZIO *z);
+
+#endif
diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h
new file mode 100644
index 0000000000..c26f4566c5
--- /dev/null
+++ b/engines/sword25/util/pluto/pdep/pdep.h
@@ -0,0 +1,41 @@
+#ifndef PDEP_H
+#define PDEP_H
+
+#include "lua.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llimits.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lauxlib.h"
+
+
+#define pdep_reallocv(L,b,on,n,e) \
+ pdep_realloc_(L, (b), (on)*(e), (n)*(e))
+#define pdep_reallocvector(L, v,oldn,n,t) \
+ ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t))))
+#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t))
+#define pdep_newvector(L,n,t) \
+ cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t)))
+#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t)))
+#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t))
+#define pdep_checkstack(L,n) \
+ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
+ pdep_growstack(L, n); \
+ else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1);
+
+
+void pdep_pushobject (lua_State *L, const TValue *o);
+void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize);
+void pdep_link (lua_State *L, GCObject *o, lu_byte tt);
+Proto *pdep_newproto (lua_State *L);
+Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e);
+void pdep_reallocstack (lua_State *L, int newsize);
+void pdep_growstack (lua_State *L, int n);
+void pdep_reallocCI (lua_State *L, int newsize);
+TString *pdep_newlstr (lua_State *L, const char *str, size_t l);
+
+#endif
diff --git a/engines/sword25/util/pluto/pluto.c b/engines/sword25/util/pluto/pluto.c
new file mode 100644
index 0000000000..61eb40e984
--- /dev/null
+++ b/engines/sword25/util/pluto/pluto.c
@@ -0,0 +1,1658 @@
+/* $Id$ */
+
+/* Pluto - Heavy-duty persistence for Lua
+ * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public
+ * domain. People making use of this software as part of an application
+ * are politely requested to email the author at sneftel@gmail.com
+ * with a brief description of the application, primarily to satisfy his
+ * curiosity.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "sword25/util/lua/lua.h"
+#include "pluto.h"
+
+#define USE_PDEP
+
+#ifdef USE_PDEP
+#include "pdep/pdep.h"
+#define LIF(prefix, name) pdep ## _ ## name
+#else
+#include "lapi.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llimits.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lauxlib.h"
+#define LIF(prefix, name) lua ## prefix ## _ ## name
+#endif
+
+#include <string.h>
+
+
+
+/* #define PLUTO_DEBUG */
+
+
+
+
+#ifdef PLUTO_DEBUG
+#include <stdio.h>
+#endif
+
+#define PLUTO_TPERMANENT 101
+
+#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); }
+
+typedef struct PersistInfo_t {
+ lua_State *L;
+ int counter;
+ lua_Chunkwriter writer;
+ void *ud;
+#ifdef PLUTO_DEBUG
+ int level;
+#endif
+} PersistInfo;
+
+#ifdef PLUTO_DEBUG
+void printindent(int indent)
+{
+ int il;
+ for(il=0; il<indent; il++) {
+ printf(" ");
+ }
+}
+#endif
+
+/* Mutual recursion requires prototype */
+static void persist(PersistInfo *pi);
+
+/* A simple reimplementation of the unfortunately static function luaA_index.
+ * Does not support the global table, registry, or upvalues. */
+static StkId getobject(lua_State *L, int stackpos)
+{
+ if(stackpos > 0) {
+ lua_assert(L->base+stackpos-1 < L->top);
+ return L->base+stackpos-1;
+ } else {
+ lua_assert(L->top-stackpos >= L->base);
+ return L->top+stackpos;
+ }
+}
+
+/* Choose whether to do a regular or special persistence based on an object's
+ * metatable. "default" is whether the object, if it doesn't have a __persist
+ * entry, is literally persistable or not.
+ * Pushes the unpersist closure and returns true if special persistence is
+ * used. */
+static int persistspecialobject(PersistInfo *pi, int defaction)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(pi->L, 4);
+ /* Check whether we should persist literally, or via the __persist
+ * metafunction */
+ if(!lua_getmetatable(pi->L, -1)) {
+ if(defaction) {
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Type not literally persistable by default");
+ lua_error(pi->L);
+ }
+ }
+ /* perms reftbl sptbl ... obj mt */
+ lua_pushstring(pi->L, "__persist");
+ /* perms reftbl sptbl ... obj mt "__persist" */
+ lua_rawget(pi->L, -2);
+ /* perms reftbl sptbl ... obj mt __persist? */
+ if(lua_isnil(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt nil */
+ lua_pop(pi->L, 2);
+ /* perms reftbl sptbl ... obj */
+ if(defaction) {
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Type not literally persistable by default");
+ lua_error(pi->L);
+ return 0; /* not reached */
+ }
+ } else if(lua_isboolean(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt bool */
+ if(lua_toboolean(pi->L, -1)) {
+ /* perms reftbl sptbl ... obj mt true */
+ lua_pop(pi->L, 2);
+ /* perms reftbl sptbl ... obj */
+ {
+ int zero = 0;
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ }
+ return 0;
+ } else {
+ lua_pushstring(pi->L, "Metatable forbade persistence");
+ lua_error(pi->L);
+ return 0; /* not reached */
+ }
+ } else if(!lua_isfunction(pi->L, -1)) {
+ lua_pushstring(pi->L, "__persist not nil, boolean, or function");
+ lua_error(pi->L);
+ }
+ /* perms reftbl ... obj mt __persist */
+ lua_pushvalue(pi->L, -3);
+ /* perms reftbl ... obj mt __persist obj */
+#ifdef PLUTO_PASS_USERDATA_TO_PERSIST
+ lua_pushlightuserdata(pi->L, (void*)pi->writer);
+ lua_pushlightuserdata(pi->L, pi->ud);
+ /* perms reftbl ... obj mt __persist obj ud */
+ lua_call(pi->L, 3, 1);
+ /* perms reftbl ... obj mt func? */
+#else
+ lua_call(pi->L, 1, 1);
+ /* perms reftbl ... obj mt func? */
+#endif
+ /* perms reftbl ... obj mt func? */
+ if(!lua_isfunction(pi->L, -1)) {
+ lua_pushstring(pi->L, "__persist function did not return a function");
+ lua_error(pi->L);
+ }
+ /* perms reftbl ... obj mt func */
+ {
+ int one = 1;
+ pi->writer(pi->L, &one, sizeof(int), pi->ud);
+ }
+ persist(pi);
+ /* perms reftbl ... obj mt func */
+ lua_pop(pi->L, 2);
+ /* perms reftbl ... obj */
+ return 1;
+}
+
+static void persisttable(PersistInfo *pi)
+{
+ /* perms reftbl ... tbl */
+ lua_checkstack(pi->L, 3);
+ if(persistspecialobject(pi, 1)) {
+ /* perms reftbl ... tbl */
+ return;
+ }
+ /* perms reftbl ... tbl */
+ /* First, persist the metatable (if any) */
+ if(!lua_getmetatable(pi->L, -1)) {
+ lua_pushnil(pi->L);
+ }
+ /* perms reftbl ... tbl mt/nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl */
+
+ /* Now, persist all k/v pairs */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... tbl nil */
+ while(lua_next(pi->L, -2)) {
+ /* perms reftbl ... tbl k v */
+ lua_pushvalue(pi->L, -2);
+ /* perms reftbl ... tbl k v k */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl k v */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl k */
+ }
+ /* perms reftbl ... tbl */
+ /* Terminate list */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... tbl nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... tbl */
+}
+
+static void persistuserdata(PersistInfo *pi) {
+ /* perms reftbl ... udata */
+ lua_checkstack(pi->L, 2);
+ if(persistspecialobject(pi, 0)) {
+ /* perms reftbl ... udata */
+ return;
+ } else {
+ /* Use literal persistence */
+ size_t length = uvalue(getobject(pi->L, -1))->len;
+ pi->writer(pi->L, &length, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, lua_touserdata(pi->L, -1), length, pi->ud);
+ if(!lua_getmetatable(pi->L, -1)) {
+ /* perms reftbl ... udata */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... udata mt/nil */
+ }
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... udata */
+ }
+}
+
+
+static Proto *toproto(lua_State *L, int stackpos)
+{
+ return gco2p(getobject(L, stackpos)->value.gc);
+}
+
+static UpVal *toupval(lua_State *L, int stackpos)
+{
+ lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL);
+ return gco2uv(getobject(L, stackpos)->value.gc);
+}
+
+static void pushproto(lua_State *L, Proto *proto)
+{
+ TValue o;
+ setptvalue(L, &o, proto);
+ LIF(A,pushobject)(L, &o);
+}
+
+#define setuvvalue(L,obj,x) \
+ { TValue *i_o=(obj); \
+ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \
+ checkliveness(G(L),i_o); }
+
+static void pushupval(lua_State *L, UpVal *upval)
+{
+ TValue o;
+ setuvvalue(L, &o, upval);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void pushclosure(lua_State *L, Closure *closure)
+{
+ TValue o;
+ setclvalue(L, &o, closure);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void pushstring(lua_State *L, TString *s)
+{
+ TValue o;
+ setsvalue(L, &o, s);
+ LIF(A,pushobject)(L, &o);
+}
+
+static void persistfunction(PersistInfo *pi)
+{
+ /* perms reftbl ... func */
+ Closure *cl = clvalue(getobject(pi->L, -1));
+ lua_checkstack(pi->L, 2);
+ if(cl->c.isC) {
+ /* It's a C function. For now, we aren't going to allow
+ * persistence of C closures, even if the "C proto" is
+ * already in the permanents table. */
+ lua_pushstring(pi->L, "Attempt to persist a C function");
+ lua_error(pi->L);
+ } else {
+ /* It's a Lua closure. */
+ {
+ /* We don't really _NEED_ the number of upvals,
+ * but it'll simplify things a bit */
+ pi->writer(pi->L, &cl->l.p->nups, sizeof(lu_byte), pi->ud);
+ }
+ /* Persist prototype */
+ {
+ pushproto(pi->L, cl->l.p);
+ /* perms reftbl ... func proto */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* Persist upvalue values (not the upvalue objects
+ * themselves) */
+ {
+ int i;
+ for(i=0; i<cl->l.p->nups; i++) {
+ /* perms reftbl ... func */
+ pushupval(pi->L, cl->l.upvals[i]);
+ /* perms reftbl ... func upval */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+ }
+ /* Persist function environment */
+ {
+ lua_getfenv(pi->L, -1);
+ /* perms reftbl ... func fenv */
+ if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) {
+ /* Function has the default fenv */
+ /* perms reftbl ... func _G */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... func nil */
+ }
+ /* perms reftbl ... func fenv/nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... func */
+ }
+ }
+}
+
+
+/* Upvalues are tricky. Here's why.
+ *
+ * A particular upvalue may be either "open", in which case its member v
+ * points into a thread's stack, or "closed" in which case it points to the
+ * upvalue itself. An upvalue is closed under any of the following conditions:
+ * -- The function that initially declared the variable "local" returns
+ * -- The thread in which the closure was created is garbage collected
+ *
+ * To make things wackier, just because a thread is reachable by Lua doesn't
+ * mean it's in our root set. We need to be able to treat an open upvalue
+ * from an unreachable thread as a closed upvalue.
+ *
+ * The solution:
+ * (a) For the purposes of persisting, don't indicate whether an upvalue is
+ * closed or not.
+ * (b) When unpersisting, pretend that all upvalues are closed.
+ * (c) When persisting, persist all open upvalues referenced by a thread
+ * that is persisted, and tag each one with the corresponding stack position
+ * (d) When unpersisting, "reopen" each of these upvalues as the thread is
+ * unpersisted
+ */
+static void persistupval(PersistInfo *pi)
+{
+ /* perms reftbl ... upval */
+ UpVal *uv = toupval(pi->L, -1);
+ lua_checkstack(pi->L, 1);
+
+ /* We can't permit the upval to linger around on the stack, as Lua
+ * will bail if its GC finds it. */
+
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... */
+ LIF(A,pushobject)(pi->L, uv->v);
+ /* perms reftbl ... obj */
+ persist(pi);
+ /* perms reftbl ... obj */
+}
+
+static void persistproto(PersistInfo *pi)
+{
+ /* perms reftbl ... proto */
+ Proto *p = toproto(pi->L, -1);
+ lua_checkstack(pi->L, 2);
+
+ /* Persist constant refs */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizek, sizeof(int), pi->ud);
+ for(i=0; i<p->sizek; i++) {
+ LIF(A,pushobject)(pi->L, &p->k[i]);
+ /* perms reftbl ... proto const */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ }
+ /* perms reftbl ... proto */
+
+ /* serialize inner Proto refs */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizep, sizeof(int), pi->ud);
+ for(i=0; i<p->sizep; i++)
+ {
+ pushproto(pi->L, p->p[i]);
+ /* perms reftbl ... proto subproto */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ }
+ /* perms reftbl ... proto */
+
+ /* Serialize code */
+ {
+ pi->writer(pi->L, &p->sizecode, sizeof(int), pi->ud);
+ pi->writer(pi->L, p->code, sizeof(Instruction) * p->sizecode, pi->ud);
+ }
+
+ /* Serialize upvalue names */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizeupvalues, sizeof(int), pi->ud);
+ for(i=0; i<p->sizeupvalues; i++)
+ {
+ pushstring(pi->L, p->upvalues[i]);
+ persist(pi);
+ lua_pop(pi->L, 1);
+ }
+ }
+ /* Serialize local variable infos */
+ {
+ int i;
+ pi->writer(pi->L, &p->sizelocvars, sizeof(int), pi->ud);
+ for(i=0; i<p->sizelocvars; i++)
+ {
+ pushstring(pi->L, p->locvars[i].varname);
+ persist(pi);
+ lua_pop(pi->L, 1);
+
+ pi->writer(pi->L, &p->locvars[i].startpc, sizeof(int), pi->ud);
+ pi->writer(pi->L, &p->locvars[i].endpc, sizeof(int), pi->ud);
+ }
+ }
+
+ /* Serialize source string */
+ pushstring(pi->L, p->source);
+ persist(pi);
+ lua_pop(pi->L, 1);
+
+ /* Serialize line numbers */
+ {
+ pi->writer(pi->L, &p->sizelineinfo, sizeof(int), pi->ud);
+ if (p->sizelineinfo)
+ {
+ pi->writer(pi->L, p->lineinfo, sizeof(int) * p->sizelineinfo, pi->ud);
+ }
+ }
+
+ /* Serialize linedefined and lastlinedefined */
+ pi->writer(pi->L, &p->linedefined, sizeof(int), pi->ud);
+ pi->writer(pi->L, &p->lastlinedefined, sizeof(int), pi->ud);
+
+ /* Serialize misc values */
+ {
+ pi->writer(pi->L, &p->nups, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->numparams, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->is_vararg, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &p->maxstacksize, sizeof(lu_byte), pi->ud);
+ }
+ /* We do not currently persist upvalue names, local variable names,
+ * variable lifetimes, line info, or source code. */
+}
+
+/* Copies a stack, but the stack is reversed in the process
+ */
+static size_t revappendstack(lua_State *from, lua_State *to)
+{
+ StkId o;
+ for(o=from->top-1; o>=from->stack; o--) {
+ setobj2s(to, to->top, o);
+ to->top++;
+ }
+ return from->top - from->stack;
+}
+
+/* Persist all stack members
+ */
+static void persistthread(PersistInfo *pi)
+{
+ size_t posremaining;
+ lua_State *L2;
+ /* perms reftbl ... thr */
+ L2 = lua_tothread(pi->L, -1);
+ lua_checkstack(pi->L, L2->top - L2->stack + 1);
+ if(pi->L == L2) {
+ lua_pushstring(pi->L, "Can't persist currently running thread");
+ lua_error(pi->L);
+ return; /* not reached */
+ }
+
+ /* Persist the stack */
+ posremaining = revappendstack(L2, pi->L);
+ /* perms reftbl ... thr (rev'ed contents of L2) */
+ pi->writer(pi->L, &posremaining, sizeof(size_t), pi->ud);
+ for(; posremaining > 0; posremaining--) {
+ persist(pi);
+ lua_pop(pi->L, 1);
+ }
+ /* perms reftbl ... thr */
+ /* Now, persist the CallInfo stack. */
+ {
+ size_t i, numframes = (L2->ci - L2->base_ci) + 1;
+ pi->writer(pi->L, &numframes, sizeof(size_t), pi->ud);
+ for(i=0; i<numframes; i++) {
+ CallInfo *ci = L2->base_ci + i;
+ size_t stackbase = ci->base - L2->stack;
+ size_t stackfunc = ci->func - L2->stack;
+ size_t stacktop = ci->top - L2->stack;
+ size_t savedpc = (ci != L2->base_ci) ?
+ ci->savedpc - ci_func(ci)->l.p->code :
+ 0;
+ pi->writer(pi->L, &stackbase, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stackfunc, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stacktop, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &ci->nresults, sizeof(int), pi->ud);
+ pi->writer(pi->L, &savedpc, sizeof(size_t), pi->ud);
+ }
+ }
+
+ /* Serialize the state's other parameters, with the exception of upval stuff */
+ {
+ size_t stackbase = L2->base - L2->stack;
+ size_t stacktop = L2->top - L2->stack;
+ lua_assert(L2->nCcalls <= 1);
+ pi->writer(pi->L, &L2->status, sizeof(lu_byte), pi->ud);
+ pi->writer(pi->L, &stackbase, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &stacktop, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, &L2->errfunc, sizeof(ptrdiff_t), pi->ud);
+ }
+
+ /* Finally, record upvalues which need to be reopened */
+ /* See the comment above persistupval() for why we do this */
+ {
+ GCObject *gco;
+ UpVal *uv;
+ /* perms reftbl ... thr */
+ for(gco = L2->openupval; gco != NULL; gco = uv->next) {
+ size_t stackpos;
+ uv = gco2uv(gco);
+
+ /* Make sure upvalue is really open */
+ lua_assert(uv->v != &uv->u.value);
+ pushupval(pi->L, uv);
+ /* perms reftbl ... thr uv */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... thr */
+ stackpos = uv->v - L2->stack;
+ pi->writer(pi->L, &stackpos, sizeof(size_t), pi->ud);
+ }
+ /* perms reftbl ... thr */
+ lua_pushnil(pi->L);
+ /* perms reftbl ... thr nil */
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... thr */
+ }
+ /* perms reftbl ... thr */
+}
+
+static void persistboolean(PersistInfo *pi)
+{
+ int b = lua_toboolean(pi->L, -1);
+ pi->writer(pi->L, &b, sizeof(int), pi->ud);
+}
+
+static void persistlightuserdata(PersistInfo *pi)
+{
+ void *p = lua_touserdata(pi->L, -1);
+ pi->writer(pi->L, &p, sizeof(void *), pi->ud);
+}
+
+static void persistnumber(PersistInfo *pi)
+{
+ lua_Number n = lua_tonumber(pi->L, -1);
+ pi->writer(pi->L, &n, sizeof(lua_Number), pi->ud);
+}
+
+static void persiststring(PersistInfo *pi)
+{
+ size_t length = lua_strlen(pi->L, -1);
+ pi->writer(pi->L, &length, sizeof(size_t), pi->ud);
+ pi->writer(pi->L, lua_tostring(pi->L, -1), length, pi->ud);
+}
+
+/* Top-level delegating persist function
+ */
+static void persist(PersistInfo *pi)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(pi->L, 2);
+ /* If the object has already been written, write a reference to it */
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_rawget(pi->L, 2);
+ /* perms reftbl ... obj ref? */
+ if(!lua_isnil(pi->L, -1)) {
+ /* perms reftbl ... obj ref */
+ int zero = 0;
+ int ref = (int)lua_touserdata(pi->L, -1);
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ pi->writer(pi->L, &ref, sizeof(int), pi->ud);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj ref */
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("0 %d\n", ref);
+#endif
+ return;
+ }
+ /* perms reftbl ... obj nil */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+ /* If the object is nil, write the pseudoreference 0 */
+ if(lua_isnil(pi->L, -1)) {
+ int zero = 0;
+ /* firsttime */
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+ /* ref */
+ pi->writer(pi->L, &zero, sizeof(int), pi->ud);
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("0 0\n");
+#endif
+ return;
+ }
+ {
+ /* indicate that it's the first time */
+ int one = 1;
+ pi->writer(pi->L, &one, sizeof(int), pi->ud);
+ }
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_pushlightuserdata(pi->L, (void*)(++(pi->counter)));
+ /* perms reftbl ... obj obj ref */
+ lua_rawset(pi->L, 2);
+ /* perms reftbl ... obj */
+
+ pi->writer(pi->L, &pi->counter, sizeof(int), pi->ud);
+
+
+ /* At this point, we'll give the permanents table a chance to play. */
+ {
+ lua_pushvalue(pi->L, -1);
+ /* perms reftbl ... obj obj */
+ lua_gettable(pi->L, 1);
+ /* perms reftbl ... obj permkey? */
+ if(!lua_isnil(pi->L, -1)) {
+ /* perms reftbl ... obj permkey */
+ int type = PLUTO_TPERMANENT;
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("1 %d PERM\n", pi->counter);
+ pi->level++;
+#endif
+ pi->writer(pi->L, &type, sizeof(int), pi->ud);
+ persist(pi);
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+#ifdef PLUTO_DEBUG
+ pi->level--;
+#endif
+ return;
+ } else {
+ /* perms reftbl ... obj nil */
+ lua_pop(pi->L, 1);
+ /* perms reftbl ... obj */
+ }
+ /* perms reftbl ... obj */
+ }
+ {
+ int type = lua_type(pi->L, -1);
+ pi->writer(pi->L, &type, sizeof(int), pi->ud);
+
+#ifdef PLUTO_DEBUG
+ printindent(pi->level);
+ printf("1 %d %d\n", pi->counter, type);
+ pi->level++;
+#endif
+ }
+
+ switch(lua_type(pi->L, -1)) {
+ case LUA_TBOOLEAN:
+ persistboolean(pi);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ persistlightuserdata(pi);
+ break;
+ case LUA_TNUMBER:
+ persistnumber(pi);
+ break;
+ case LUA_TSTRING:
+ persiststring(pi);
+ break;
+ case LUA_TTABLE:
+ persisttable(pi);
+ break;
+ case LUA_TFUNCTION:
+ persistfunction(pi);
+ break;
+ case LUA_TTHREAD:
+ persistthread(pi);
+ break;
+ case LUA_TPROTO:
+ persistproto(pi);
+ break;
+ case LUA_TUPVAL:
+ persistupval(pi);
+ break;
+ case LUA_TUSERDATA:
+ persistuserdata(pi);
+ break;
+ default:
+ lua_assert(0);
+ }
+#ifdef PLUTO_DEBUG
+ pi->level--;
+#endif
+}
+
+void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud)
+{
+ PersistInfo pi;
+
+ pi.counter = 0;
+ pi.L = L;
+ pi.writer = writer;
+ pi.ud = ud;
+#ifdef PLUTO_DEBUG
+ pi.level = 0;
+#endif
+
+ lua_checkstack(L, 4);
+ /* perms? rootobj? ...? */
+ lua_assert(lua_gettop(L) == 2);
+ /* perms rootobj */
+ lua_assert(!lua_isnil(L, 2));
+ /* perms rootobj */
+ lua_newtable(L);
+ /* perms rootobj reftbl */
+
+ /* Now we're going to make the table weakly keyed. This prevents the
+ * GC from visiting it and trying to mark things it doesn't want to
+ * mark in tables, e.g. upvalues. All objects in the table are
+ * a priori reachable, so it doesn't matter that we do this. */
+ lua_newtable(L);
+ /* perms rootobj reftbl mt */
+ lua_pushstring(L, "__mode");
+ /* perms rootobj reftbl mt "__mode" */
+ lua_pushstring(L, "k");
+ /* perms rootobj reftbl mt "__mode" "k" */
+ lua_settable(L, 4);
+ /* perms rootobj reftbl mt */
+ lua_setmetatable(L, 3);
+ /* perms rootobj reftbl */
+ lua_insert(L, 2);
+ /* perms reftbl rootobj */
+ persist(&pi);
+ /* perms reftbl rootobj */
+ lua_remove(L, 2);
+ /* perms rootobj */
+}
+
+typedef struct WriterInfo_t {
+ char* buf;
+ size_t buflen;
+} WriterInfo;
+
+static int bufwriter (lua_State *L, const void* p, size_t sz, void* ud) {
+ const char* cp = (const char*)p;
+ WriterInfo *wi = (WriterInfo *)ud;
+
+ LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char);
+ while(sz)
+ {
+ /* how dearly I love ugly C pointer twiddling */
+ wi->buf[wi->buflen++] = *cp++;
+ sz--;
+ }
+ return 0;
+}
+
+int persist_l(lua_State *L)
+{
+ /* perms? rootobj? ...? */
+ WriterInfo wi;
+
+ wi.buf = NULL;
+ wi.buflen = 0;
+
+ lua_settop(L, 2);
+ /* perms? rootobj? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms rootobj? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms rootobj */
+
+ pluto_persist(L, bufwriter, &wi);
+
+ lua_settop(L, 0);
+ /* (empty) */
+ lua_pushlstring(L, wi.buf, wi.buflen);
+ /* str */
+ pdep_freearray(L, wi.buf, wi.buflen, char);
+ return 1;
+}
+
+typedef struct UnpersistInfo_t {
+ lua_State *L;
+ ZIO zio;
+#ifdef PLUTO_DEBUG
+ int level;
+#endif
+} UnpersistInfo;
+
+static void unpersist(UnpersistInfo *upi);
+
+/* The object is left on the stack. This is primarily used by unpersist, but
+ * may be used by GCed objects that may incur cycles in order to preregister
+ * the object. */
+static void registerobject(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... obj */
+ lua_checkstack(upi->L, 2);
+ lua_pushlightuserdata(upi->L, (void*)ref);
+ /* perms reftbl ... obj ref */
+ lua_pushvalue(upi->L, -2);
+ /* perms reftbl ... obj ref obj */
+ lua_settable(upi->L, 2);
+ /* perms reftbl ... obj */
+}
+
+static void unpersistboolean(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int b;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0);
+ lua_pushboolean(upi->L, b);
+ /* perms reftbl ... bool */
+}
+
+static void unpersistlightuserdata(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ void *p;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0);
+ lua_pushlightuserdata(upi->L, p);
+ /* perms reftbl ... ludata */
+}
+
+static void unpersistnumber(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_Number n;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0);
+ lua_pushnumber(upi->L, n);
+ /* perms reftbl ... num */
+}
+
+static void unpersiststring(UnpersistInfo *upi)
+{
+ /* perms reftbl sptbl ref */
+ int length;
+ char* string;
+ lua_checkstack(upi->L, 1);
+ verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);
+ string = pdep_newvector(upi->L, length, char);
+ verify(LIF(Z,read)(&upi->zio, string, length) == 0);
+ lua_pushlstring(upi->L, string, length);
+ /* perms reftbl sptbl ref str */
+ pdep_freearray(upi->L, string, length, char);
+}
+
+static void unpersistspecialtable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 1);
+ unpersist(upi);
+ /* perms reftbl ... spfunc? */
+ lua_assert(lua_isfunction(upi->L, -1));
+ /* perms reftbl ... spfunc */
+ lua_call(upi->L, 0, 1);
+ /* perms reftbl ... tbl? */
+ lua_assert(lua_istable(upi->L, -1));
+ /* perms reftbl ... tbl */
+}
+
+static void unpersistliteraltable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 3);
+ /* Preregister table for handling of cycles */
+ lua_newtable(upi->L);
+ /* perms reftbl ... tbl */
+ registerobject(ref, upi);
+ /* perms reftbl ... tbl */
+ /* Unpersist metatable */
+ {
+ unpersist(upi);
+ /* perms reftbl ... tbl mt/nil? */
+ if(lua_istable(upi->L, -1)) {
+ /* perms reftbl ... tbl mt */
+ lua_setmetatable(upi->L, -2);
+ /* perms reftbl ... tbl */
+ } else {
+ /* perms reftbl ... tbl nil? */
+ lua_assert(lua_isnil(upi->L, -1));
+ /* perms reftbl ... tbl nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... tbl */
+ }
+ /* perms reftbl ... tbl */
+ }
+
+ while(1)
+ {
+ /* perms reftbl ... tbl */
+ unpersist(upi);
+ /* perms reftbl ... tbl key/nil */
+ if(lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... tbl nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... tbl */
+ break;
+ }
+ /* perms reftbl ... tbl key */
+ unpersist(upi);
+ /* perms reftbl ... tbl key value? */
+ lua_assert(!lua_isnil(upi->L, -1));
+ /* perms reftbl ... tbl key value */
+ lua_rawset(upi->L, -3);
+ /* perms reftbl ... tbl */
+ }
+}
+
+static void unpersisttable(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 1);
+ {
+ int isspecial;
+ verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0);
+ if(isspecial) {
+ unpersistspecialtable(ref, upi);
+ /* perms reftbl ... tbl */
+ } else {
+ unpersistliteraltable(ref, upi);
+ /* perms reftbl ... tbl */
+ }
+ /* perms reftbl ... tbl */
+ }
+}
+
+static UpVal *makeupval(lua_State *L, int stackpos)
+{
+ UpVal *uv = pdep_new(L, UpVal);
+ pdep_link(L, (GCObject*)uv, LUA_TUPVAL);
+ uv->tt = LUA_TUPVAL;
+ uv->v = &uv->u.value;
+ uv->u.l.prev = NULL;
+ uv->u.l.next = NULL;
+ setobj(L, uv->v, getobject(L, stackpos));
+ return uv;
+}
+
+static Proto *makefakeproto(lua_State *L, lu_byte nups)
+{
+ Proto *p = pdep_newproto(L);
+ p->sizelineinfo = 1;
+ p->lineinfo = pdep_newvector(L, 1, int);
+ p->lineinfo[0] = 1;
+ p->sizecode = 1;
+ p->code = pdep_newvector(L, 1, Instruction);
+ p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0);
+ p->source = pdep_newlstr(L, "", 0);
+ p->maxstacksize = 2;
+ p->nups = nups;
+ p->sizek = 0;
+ p->sizep = 0;
+
+ return p;
+}
+
+/* The GC is not fond of finding upvalues in tables. We get around this
+ * during persistence using a weakly keyed table, so that the GC doesn't
+ * bother to mark them. This won't work in unpersisting, however, since
+ * if we make the values weak they'll be collected (since nothing else
+ * references them). Our solution, during unpersisting, is to represent
+ * upvalues as dummy functions, each with one upvalue. */
+static void boxupval_start(lua_State *L)
+{
+ LClosure *lcl;
+ lcl = (LClosure*)pdep_newLclosure(L, 1, hvalue(&L->l_gt));
+ pushclosure(L, (Closure*)lcl);
+ /* ... func */
+ lcl->p = makefakeproto(L, 1);
+
+ /* Temporarily initialize the upvalue to nil */
+
+ lua_pushnil(L);
+ lcl->upvals[0] = makeupval(L, -1);
+ lua_pop(L, 1);
+}
+
+static void boxupval_finish(lua_State *L)
+{
+ /* ... func obj */
+ LClosure *lcl = (LClosure *) clvalue(getobject(L, -2));
+
+ lcl->upvals[0]->u.value = *getobject(L, -1);
+ lua_pop(L, 1);
+}
+
+
+static void unboxupval(lua_State *L)
+{
+ /* ... func */
+ LClosure *lcl;
+ UpVal *uv;
+
+ lcl = (LClosure*)clvalue(getobject(L, -1));
+ uv = lcl->upvals[0];
+ lua_pop(L, 1);
+ /* ... */
+ pushupval(L, uv);
+ /* ... upval */
+}
+
+static void unpersistfunction(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ LClosure *lcl;
+ int i;
+ lu_byte nupvalues;
+ lua_checkstack(upi->L, 2);
+
+ verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0);
+
+ lcl = (LClosure*)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt));
+ pushclosure(upi->L, (Closure*)lcl);
+
+ /* perms reftbl ... func */
+ /* Put *some* proto in the closure, before the GC can find it */
+ lcl->p = makefakeproto(upi->L, nupvalues);
+
+ /* Also, we need to temporarily fill the upvalues */
+ lua_pushnil(upi->L);
+ /* perms reftbl ... func nil */
+ for(i=0; i<nupvalues; i++) {
+ lcl->upvals[i] = makeupval(upi->L, -1);
+ }
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+
+ /* I can't see offhand how a function would ever get to be self-
+ * referential, but just in case let's register it early */
+ registerobject(ref, upi);
+
+ /* Now that it's safe, we can get the real proto */
+ unpersist(upi);
+ /* perms reftbl ... func proto? */
+ lua_assert(lua_type(upi->L, -1) == LUA_TPROTO);
+ /* perms reftbl ... func proto */
+ lcl->p = toproto(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+
+ for(i=0; i<nupvalues; i++) {
+ /* perms reftbl ... func */
+ unpersist(upi);
+ /* perms reftbl ... func func2 */
+ unboxupval(upi->L);
+ /* perms reftbl ... func upval */
+ lcl->upvals[i] = toupval(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+
+ /* Finally, the fenv */
+ unpersist(upi);
+ /* perms reftbl ... func fenv/nil? */
+ lua_assert(lua_type(upi->L, -1) == LUA_TNIL ||
+ lua_type(upi->L, -1) == LUA_TTABLE);
+ /* perms reftbl ... func fenv/nil */
+ if(!lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... func fenv */
+ lua_setfenv(upi->L, -2);
+ /* perms reftbl ... func */
+ } else {
+ /* perms reftbl ... func nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... func */
+ }
+ /* perms reftbl ... func */
+}
+
+static void unpersistupval(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 2);
+
+ boxupval_start(upi->L);
+ /* perms reftbl ... func */
+ registerobject(ref, upi);
+
+ unpersist(upi);
+ /* perms reftbl ... func obj */
+ boxupval_finish(upi->L);
+ /* perms reftbl ... func */
+}
+
+static void unpersistproto(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ Proto *p;
+ int i;
+ int sizep, sizek;
+
+ /* We have to be careful. The GC expects a lot out of protos. In
+ * particular, we need to give the function a valid string for its
+ * source, and valid code, even before we actually read in the real
+ * code. */
+ TString *source = pdep_newlstr(upi->L, "", 0);
+ p = pdep_newproto(upi->L);
+ p->source = source;
+ p->sizecode=1;
+ p->code = pdep_newvector(upi->L, 1, Instruction);
+ p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0);
+ p->maxstacksize = 2;
+ p->sizek = 0;
+ p->sizep = 0;
+
+ lua_checkstack(upi->L, 2);
+
+ pushproto(upi->L, p);
+ /* perms reftbl ... proto */
+ /* We don't need to register early, since protos can never ever be
+ * involved in cyclic references */
+
+ /* Read in constant references */
+ {
+ verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue);
+ for(i=0; i<sizek; i++) {
+ /* perms reftbl ... proto */
+ unpersist(upi);
+ /* perms reftbl ... proto k */
+ setobj2s(upi->L, &p->k[i], getobject(upi->L, -1));
+ p->sizek++;
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ /* perms reftbl ... proto */
+ }
+ /* Read in sub-proto references */
+ {
+ verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*);
+ for(i=0; i<sizep; i++) {
+ /* perms reftbl ... proto */
+ unpersist(upi);
+ /* perms reftbl ... proto subproto */
+ p->p[i] = toproto(upi->L, -1);
+ p->sizep++;
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... proto */
+ }
+ /* perms reftbl ... proto */
+ }
+
+ /* Read in code */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0);
+ LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction);
+ verify(LIF(Z,read)(&upi->zio, p->code,
+ sizeof(Instruction) * p->sizecode) == 0);
+ }
+
+ /* Read in upvalue names */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0);
+ if (p->sizeupvalues)
+ {
+ LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *);
+ for(i=0; i<p->sizeupvalues; i++)
+ {
+ unpersist(upi);
+ p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+ }
+ }
+ }
+
+ /* Read in local variable infos */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0);
+ if (p->sizelocvars)
+ {
+ LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar);
+ for(i=0; i<p->sizelocvars; i++)
+ {
+ unpersist(upi);
+ p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+
+ verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0);
+ }
+ }
+ }
+
+ /* Read in source string*/
+ unpersist(upi);
+ p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1)));
+ lua_pop(upi->L, 1);
+
+ /* Read in line numbers */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0);
+ if (p->sizelineinfo)
+ {
+ LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int);
+ verify(LIF(Z,read)(&upi->zio, p->lineinfo,
+ sizeof(int) * p->sizelineinfo) == 0);
+ }
+ }
+
+ /* Read in linedefined and lastlinedefined */
+ verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0);
+
+ /* Read in misc values */
+ {
+ verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0);
+ }
+}
+
+
+/* Does basically the opposite of luaC_link().
+ * Right now this function is rather inefficient; it requires traversing the
+ * entire root GC set in order to find one object. If the GC list were doubly
+ * linked this would be much easier, but there's no reason for Lua to have
+ * that. */
+static void gcunlink(lua_State *L, GCObject *gco)
+{
+ GCObject *prevslot;
+ if(G(L)->rootgc == gco) {
+ G(L)->rootgc = G(L)->rootgc->gch.next;
+ return;
+ }
+
+ prevslot = G(L)->rootgc;
+ while(prevslot->gch.next != gco) {
+ lua_assert(prevslot->gch.next != NULL);
+ prevslot = prevslot->gch.next;
+ }
+
+ prevslot->gch.next = prevslot->gch.next->gch.next;
+}
+
+/* FIXME __ALL__ field ordering */
+static void unpersistthread(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_State *L2;
+ size_t stacklimit = 0;
+ L2 = lua_newthread(upi->L);
+ lua_checkstack(upi->L, 3);
+ /* L1: perms reftbl ... thr */
+ /* L2: (empty) */
+ registerobject(ref, upi);
+
+ /* First, deserialize the object stack. */
+ {
+ size_t i, stacksize;
+ verify(LIF(Z,read)(&upi->zio, &stacksize, sizeof(size_t)) == 0);
+ LIF(D,growstack)(L2, (int)stacksize);
+ /* Make sure that the first stack element (a nil, representing
+ * the imaginary top-level C function) is written to the very,
+ * very bottom of the stack */
+ L2->top--;
+ for(i=0; i<stacksize; i++) {
+ unpersist(upi);
+ /* L1: perms reftbl ... thr obj* */
+ }
+ lua_xmove(upi->L, L2, stacksize);
+ /* L1: perms reftbl ... thr */
+ /* L2: obj* */
+ }
+ /* (hereafter, stacks refer to L1) */
+
+ /* Now, deserialize the CallInfo stack. */
+ {
+ size_t i, numframes;
+ verify(LIF(Z,read)(&upi->zio, &numframes, sizeof(size_t)) == 0);
+ LIF(D,reallocCI)(L2,numframes*2);
+ for(i=0; i<numframes; i++) {
+ CallInfo *ci = L2->base_ci + i;
+ size_t stackbase, stackfunc, stacktop, savedpc;
+ verify(LIF(Z,read)(&upi->zio, &stackbase, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stackfunc, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stacktop, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &savedpc, sizeof(size_t)) == 0);
+
+ if(stacklimit < stacktop)
+ stacklimit = stacktop;
+
+ ci->base = L2->stack+stackbase;
+ ci->func = L2->stack+stackfunc;
+ ci->top = L2->stack+stacktop;
+ ci->savedpc = (ci != L2->base_ci) ?
+ ci_func(ci)->l.p->code+savedpc :
+ 0;
+ ci->tailcalls = 0;
+ /* Update the pointer each time, to keep the GC
+ * happy*/
+ L2->ci = ci;
+ }
+ }
+ /* perms reftbl ... thr */
+ /* Deserialize the state's other parameters, with the exception of upval stuff */
+ {
+ size_t stackbase, stacktop;
+ L2->savedpc = L2->ci->savedpc;
+ verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stackbase, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &stacktop, sizeof(size_t)) == 0);
+ verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0);
+ L2->base = L2->stack + stackbase;
+ L2->top = L2->stack + stacktop;
+ }
+ /* Finally, "reopen" upvalues (see persistupval() for why) */
+ {
+ UpVal* uv;
+ GCObject **nextslot = &L2->openupval;
+ global_State *g = G(L2);
+ while(1) {
+ size_t stackpos;
+ unpersist(upi);
+ /* perms reftbl ... thr uv/nil */
+ if(lua_isnil(upi->L, -1)) {
+ /* perms reftbl ... thr nil */
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... thr */
+ break;
+ }
+ /* perms reftbl ... thr boxeduv */
+ unboxupval(upi->L);
+ /* perms reftbl ... thr uv */
+ uv = toupval(upi->L, -1);
+ lua_pop(upi->L, 1);
+ /* perms reftbl ... thr */
+
+ verify(LIF(Z,read)(&upi->zio, &stackpos, sizeof(size_t)) == 0);
+ uv->v = L2->stack + stackpos;
+ gcunlink(upi->L, (GCObject*)uv);
+ uv->marked = luaC_white(g);
+ *nextslot = (GCObject*)uv;
+ nextslot = &uv->next;
+ uv->u.l.prev = &G(L2)->uvhead;
+ uv->u.l.next = G(L2)->uvhead.u.l.next;
+ uv->u.l.next->u.l.prev = uv;
+ G(L2)->uvhead.u.l.next = uv;
+ lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
+ }
+ *nextslot = NULL;
+ }
+
+ /* The stack must be valid at least to the highest value among the CallInfos */
+ /* 'top' and the values up to there must be filled with 'nil' */
+ {
+ StkId o;
+ LIF(D,checkstack)(L2, (int)stacklimit);
+ for (o = L2->top; o <= L2->top + stacklimit; o++)
+ setnilvalue(o);
+ }
+}
+
+static void unpersistuserdata(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int isspecial;
+ lua_checkstack(upi->L, 2);
+ verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0);
+ if(isspecial) {
+ unpersist(upi);
+ /* perms reftbl ... spfunc? */
+ lua_assert(lua_isfunction(upi->L, -1));
+ /* perms reftbl ... spfunc */
+#ifdef PLUTO_PASS_USERDATA_TO_PERSIST
+ lua_pushlightuserdata(upi->L, &upi->zio);
+ lua_call(upi->L, 1, 1);
+#else
+ lua_call(upi->L, 0, 1);
+#endif
+ /* perms reftbl ... udata? */
+/* This assertion might not be necessary; it's conceivable, for
+ * example, that the SP function might decide to return a table
+ * with equivalent functionality. For the time being, we'll
+ * ignore this possibility in favor of stricter and more testable
+ * requirements. */
+ lua_assert(lua_isuserdata(upi->L, -1));
+ /* perms reftbl ... udata */
+ } else {
+ size_t length;
+ verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);
+
+ lua_newuserdata(upi->L, length);
+ /* perms reftbl ... udata */
+ registerobject(ref, upi);
+ verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0);
+
+ unpersist(upi);
+ /* perms reftbl ... udata mt/nil? */
+ lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1));
+ /* perms reftbl ... udata mt/nil */
+ lua_setmetatable(upi->L, -2);
+ /* perms reftbl ... udata */
+ }
+ /* perms reftbl ... udata */
+}
+
+static void unpersistpermanent(int ref, UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ lua_checkstack(upi->L, 2);
+ unpersist(upi);
+ /* perms reftbl permkey */
+ lua_gettable(upi->L, 1);
+ /* perms reftbl perm? */
+ /* We assume currently that the substituted permanent value
+ * shouldn't be nil. This may be a bad assumption. Real-life
+ * experience is needed to evaluate this. */
+ lua_assert(!lua_isnil(upi->L, -1));
+ /* perms reftbl perm */
+}
+
+/* For debugging only; not called when lua_assert is empty */
+static int inreftable(lua_State *L, int ref)
+{
+ int res;
+ lua_checkstack(L, 1);
+ /* perms reftbl ... */
+ lua_pushlightuserdata(L, (void*)ref);
+ /* perms reftbl ... ref */
+ lua_gettable(L, 2);
+ /* perms reftbl ... obj? */
+ res = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ /* perms reftbl ... */
+ return res;
+}
+
+static void unpersist(UnpersistInfo *upi)
+{
+ /* perms reftbl ... */
+ int firstTime;
+ int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */
+ lua_checkstack(upi->L, 2);
+ LIF(Z,read)(&upi->zio, &firstTime, sizeof(int));
+ if(firstTime) {
+ int ref;
+ int type;
+ LIF(Z,read)(&upi->zio, &ref, sizeof(int));
+ lua_assert(!inreftable(upi->L, ref));
+ LIF(Z,read)(&upi->zio, &type, sizeof(int));
+#ifdef PLUTO_DEBUG
+ printindent(upi->level);
+ printf("1 %d %d\n", ref, type);
+ upi->level++;
+#endif
+ switch(type) {
+ case LUA_TBOOLEAN:
+ unpersistboolean(upi);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ unpersistlightuserdata(upi);
+ break;
+ case LUA_TNUMBER:
+ unpersistnumber(upi);
+ break;
+ case LUA_TSTRING:
+ unpersiststring(upi);
+ break;
+ case LUA_TTABLE:
+ unpersisttable(ref, upi);
+ break;
+ case LUA_TFUNCTION:
+ unpersistfunction(ref, upi);
+ break;
+ case LUA_TTHREAD:
+ unpersistthread(ref, upi);
+ break;
+ case LUA_TPROTO:
+ unpersistproto(ref, upi);
+ break;
+ case LUA_TUPVAL:
+ unpersistupval(ref, upi);
+ break;
+ case LUA_TUSERDATA:
+ unpersistuserdata(ref, upi);
+ break;
+ case PLUTO_TPERMANENT:
+ unpersistpermanent(ref, upi);
+ break;
+ default:
+ lua_assert(0);
+ }
+ /* perms reftbl ... obj */
+ lua_assert(lua_type(upi->L, -1) == type ||
+ type == PLUTO_TPERMANENT ||
+ /* Remember, upvalues get a special dispensation, as
+ * described in boxupval */
+ (lua_type(upi->L, -1) == LUA_TFUNCTION &&
+ type == LUA_TUPVAL));
+ registerobject(ref, upi);
+ /* perms reftbl ... obj */
+#ifdef PLUTO_DEBUG
+ upi->level--;
+#endif
+ } else {
+ int ref;
+ LIF(Z,read)(&upi->zio, &ref, sizeof(int));
+#ifdef PLUTO_DEBUG
+ printindent(upi->level);
+ printf("0 %d\n", ref);
+#endif
+ if(ref == 0) {
+ lua_pushnil(upi->L);
+ /* perms reftbl ... nil */
+ } else {
+ lua_pushlightuserdata(upi->L, (void*)ref);
+ /* perms reftbl ... ref */
+ lua_gettable(upi->L, 2);
+ /* perms reftbl ... obj? */
+ lua_assert(!lua_isnil(upi->L, -1));
+ }
+ /* perms reftbl ... obj/nil */
+ }
+ /* perms reftbl ... obj/nil */
+ lua_assert(lua_gettop(upi->L) == stacksize + 1);
+}
+
+void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud)
+{
+ /* We use the graciously provided ZIO (what the heck does the Z stand
+ * for?) library so that we don't have to deal with the reader directly.
+ * Letting the reader function decide how much data to return can be
+ * very unpleasant.
+ */
+ UnpersistInfo upi;
+ upi.L = L;
+#ifdef PLUTO_DEBUG
+ upi.level = 0;
+#endif
+
+ lua_checkstack(L, 3);
+ LIF(Z,init)(L, &upi.zio, reader, ud);
+
+ /* perms */
+ lua_newtable(L);
+ /* perms reftbl */
+ lua_gc(L, LUA_GCSTOP, 0);
+ unpersist(&upi);
+ lua_gc(L, LUA_GCRESTART, 0);
+ /* perms reftbl rootobj */
+ lua_replace(L, 2);
+ /* perms rootobj */
+}
+
+typedef struct LoadInfo_t {
+ char *buf;
+ size_t size;
+} LoadInfo;
+
+
+static const char *bufreader(lua_State *L, void *ud, size_t *sz) {
+ LoadInfo *li = (LoadInfo *)ud;
+ if(li->size == 0) {
+ return NULL;
+ }
+ *sz = li->size;
+ li->size = 0;
+ return li->buf;
+}
+
+int unpersist_l(lua_State *L)
+{
+ LoadInfo li;
+ char const *origbuf;
+ char *tempbuf;
+ size_t bufsize;
+ /* perms? str? ...? */
+ lua_settop(L, 2);
+ /* perms? str? */
+ origbuf = luaL_checklstring(L, 2, &bufsize);
+ tempbuf = LIF(M,newvector)(L, bufsize, char);
+ memcpy(tempbuf, origbuf, bufsize);
+
+ li.buf = tempbuf;
+ li.size = bufsize;
+
+ /* perms? str */
+ lua_pop(L, 1);
+ /* perms? */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* perms */
+ pluto_unpersist(L, bufreader, &li);
+ /* perms rootobj */
+ LIF(M,freearray)(L, tempbuf, bufsize, char);
+ return 1;
+}
+
+static luaL_reg pluto_reg[] = {
+ { "persist", persist_l },
+ { "unpersist", unpersist_l },
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_pluto(lua_State *L) {
+ luaL_openlib(L, "pluto", pluto_reg, 0);
+ return 1;
+}
diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h
new file mode 100644
index 0000000000..3674842d44
--- /dev/null
+++ b/engines/sword25/util/pluto/pluto.h
@@ -0,0 +1,25 @@
+/* $Id$ */
+
+/* Pluto - Heavy-duty persistence for Lua
+ * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public
+ * domain. People making use of this software as part of an application
+ * are politely requested to email the author at sneftel@gmail.com
+ * with a brief description of the application, primarily to satisfy his
+ * curiosity.
+ *
+ * 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.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* lua.h must be included before this file */
+
+void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud);
+
+void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud);
+
+LUALIB_API int luaopen_pluto(lua_State *L);
diff --git a/engines/sword25/util/pluto/plzio.c b/engines/sword25/util/pluto/plzio.c
new file mode 100644
index 0000000000..7c5ab3b773
--- /dev/null
+++ b/engines/sword25/util/pluto/plzio.c
@@ -0,0 +1,76 @@
+/*
+** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
+** a generic input stream interface
+** See Copyright Notice in lua.h
+*/
+
+
+#include <string.h>
+
+#define lzio_c
+#define LUA_CORE
+
+#include "pdep/pdep.h"
+
+int pdep_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0) return EOZ;
+ z->n = size - 1;
+ z->p = buff;
+ return char2int(*(z->p++));
+}
+
+
+int pdep_lookahead (ZIO *z) {
+ if (z->n == 0) {
+ if (pdep_fill(z) == EOZ)
+ return EOZ;
+ else {
+ z->n++; /* pdep_fill removed first byte; put back it */
+ z->p--;
+ }
+ }
+ return char2int(*z->p);
+}
+
+
+void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t pdep_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (pdep_lookahead(z) == EOZ)
+ return n; /* return number of missing bytes */
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) {
+ if (n > buff->buffsize) {
+ if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;
+ pdep_resizebuffer(L, buff, n);
+ }
+ return buff->buffer;
+}
+
+
diff --git a/engines/sword25/util/pluto/pptest.c b/engines/sword25/util/pluto/pptest.c
new file mode 100644
index 0000000000..1bfecf2b75
--- /dev/null
+++ b/engines/sword25/util/pluto/pptest.c
@@ -0,0 +1,95 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+static int LUAF_createludata(lua_State *L)
+{
+ lua_pushlightuserdata(L, (void*)321);
+ return 1;
+}
+
+/* A userdata that may be literally persisted */
+static int LUAF_boxinteger(lua_State *L)
+{
+ /* num */
+ int* ptr = lua_newuserdata(L, sizeof(int));
+ /* num udata */
+ *ptr = luaL_checkint(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_pushboolean(L, 1);
+ /* num udata mt "__persist" true */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_boxboolean(lua_State *L)
+{
+ /* bool */
+ char* ptr = lua_newuserdata(L, sizeof(char));
+ /* bool udata */
+ *ptr = lua_toboolean(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_getglobal(L, "booleanpersist");
+ /* num udata mt "__persist" booleanpersist */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_unboxboolean(lua_State *L)
+{
+ /* udata */
+ lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
+ /* udata bool */
+ return 1;
+}
+
+static int LUAF_onerror(lua_State *L)
+{
+
+ const char* str = 0;
+ if(lua_gettop(L) != 0)
+ {
+ str = lua_tostring(L, -1);
+ printf("%s\n",str);
+ }
+ return 0;
+}
+
+int main()
+{
+ lua_State* L = lua_open();
+
+ luaL_openlibs(L);
+ lua_settop(L, 0);
+
+ lua_register(L, "createludata", LUAF_createludata);
+ lua_register(L, "boxinteger", LUAF_boxinteger);
+ lua_register(L, "boxboolean", LUAF_boxboolean);
+ lua_register(L, "unboxboolean", LUAF_unboxboolean);
+ lua_register(L, "onerror", LUAF_onerror);
+
+ lua_pushcfunction(L, LUAF_onerror);
+ luaL_loadfile(L, "pptest.lua");
+ lua_pcall(L,0,0,1);
+
+ lua_close(L);
+
+ return 0;
+}
diff --git a/engines/sword25/util/pluto/pptest.lua b/engines/sword25/util/pluto/pptest.lua
new file mode 100644
index 0000000000..144da3ee80
--- /dev/null
+++ b/engines/sword25/util/pluto/pptest.lua
@@ -0,0 +1,168 @@
+-- $Id$
+
+require "pluto"
+
+permtable = { 1234 }
+
+perms = { [coroutine.yield] = 1, [permtable] = 2 }
+
+twithmt = {}
+setmetatable( twithmt, { __call = function() return 21 end } )
+
+function testfenv()
+ return abc
+end
+
+setfenv(testfenv, { abc = 456 })
+
+function fa(i)
+ local ia = i + 1
+ return fb(ia)
+end
+
+function fb(i)
+ local ib = i + 1
+ ib = ib + fc(ib)
+ return ib
+end
+
+function fc(i)
+ local ic = i + 1
+ coroutine.yield()
+ return ic*2
+end
+
+function func()
+ return 4
+end
+
+thr = coroutine.create(fa)
+coroutine.resume(thr, 2)
+
+testtbl = { a = 2, [2] = 4 }
+
+function funcreturningclosure(n)
+ return function()
+ return n
+ end
+end
+
+function nestedfunc(n)
+ return (function(m) return m+2 end)(n+3)
+end
+
+testloopa = {}
+testloopb = { testloopa = testloopa }
+testloopa.testloopb = testloopb
+
+sharedref = {}
+refa = {sharedref = sharedref}
+refb = {sharedref = sharedref}
+
+sptable = { a = 3 }
+
+setmetatable(sptable, {
+ __persist = function(tbl)
+ local a = tbl.a
+ return function()
+ return { a = a+3 }
+ end
+ end
+})
+
+literaludata = boxinteger(71)
+
+function booleanpersist(udata)
+ local b = unboxboolean(udata)
+ return function()
+ return boxboolean(b)
+ end
+end
+
+function makecounter()
+ local a = 0
+ return {
+ inc = function() a = a + 1 end,
+ cur = function() return a end
+ }
+end
+
+function uvinthreadfunc()
+ local a = 1
+ local b = function()
+ a = a+1
+ coroutine.yield()
+ a = a+1
+ end
+ a = a+1
+ b()
+ a = a+1
+ return a
+end
+
+uvinthread = coroutine.create(uvinthreadfunc)
+coroutine.resume(uvinthread)
+
+niinmt = { a = 3 }
+setmetatable(niinmt, {__newindex = function(key, val) end })
+
+
+
+
+local function GenerateObjects()
+ local Table = {}
+
+ function Table:Func()
+ return { Table, self }
+ end
+
+ function uvcycle()
+ return Table:Func()
+ end
+end
+
+GenerateObjects()
+
+
+
+function debuginfo(foo)
+ foo = foo + foo
+ return debug.getlocal(1,1)
+end
+
+rootobj = {
+ testfalse = false,
+ testtrue = true,
+ testseven = 7,
+ testfoobar = "foobar",
+ testfuncreturnsfour = func,
+ testnil = nil,
+ testthread = thr,
+ testperm = permtable,
+ testmt = twithmt,
+ testtbl = testtbl,
+ testfenv = testfenv,
+ testclosure = funcreturningclosure(11),
+ testnilclosure = funcreturningclosure(nil),
+ testnest = nestedfunc,
+ testludata = createludata(),
+ testlooptable = testloopa,
+ testsharedrefa = refa,
+ testsharedrefb = refb,
+ testsptable = sptable,
+ testliteraludata = literaludata,
+ testspudata1 = boxboolean(true),
+ testspudata2 = boxboolean(false),
+ testsharedupval = makecounter(),
+ testuvinthread = uvinthread,
+ testniinmt = niinmt,
+ testuvcycle = uvcycle,
+ testdebuginfo = debuginfo
+}
+
+buf = pluto.persist(perms, rootobj)
+
+onerror()
+outfile = io.open("test.plh", "wb")
+outfile:write(buf)
+outfile:close()
diff --git a/engines/sword25/util/pluto/puptest.c b/engines/sword25/util/pluto/puptest.c
new file mode 100644
index 0000000000..e9aa7ea305
--- /dev/null
+++ b/engines/sword25/util/pluto/puptest.c
@@ -0,0 +1,81 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+static int LUAF_checkludata(lua_State *L)
+{
+ lua_pushboolean(L, lua_touserdata(L, -1) == (void*)321);
+ return 1;
+}
+
+static int LUAF_unboxinteger(lua_State *L)
+{
+ lua_pushnumber(L, *((int*)lua_touserdata(L, -1)));
+ return 1;
+}
+
+static int LUAF_unboxboolean(lua_State *L)
+{
+ /* udata */
+ lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
+ /* udata bool */
+ return 1;
+}
+
+static int LUAF_boxboolean(lua_State *L)
+{
+ /* bool */
+ char* ptr = lua_newuserdata(L, sizeof(char));
+ /* bool udata */
+ *ptr = lua_toboolean(L, 1);
+ lua_newtable(L);
+ /* num udata mt */
+ lua_pushstring(L, "__persist");
+ /* num udata mt "__persist" */
+ lua_getglobal(L, "booleanpersist");
+ /* num udata mt "__persist" booleanpersist */
+ lua_rawset(L, 3);
+ /* num udata mt */
+ lua_setmetatable(L, 2);
+ /* num udata */
+ return 1;
+}
+
+static int LUAF_onerror(lua_State *L)
+{
+
+ const char* str = 0;
+ if(lua_gettop(L) != 0)
+ {
+ str = lua_tostring(L, -1);
+ printf("%s\n",str);
+ }
+ return 0;
+}
+
+int main()
+{
+ lua_State* L = lua_open();
+
+ luaL_openlibs(L);
+ lua_settop(L, 0);
+
+ lua_register(L, "checkludata", LUAF_checkludata);
+ lua_register(L, "unboxinteger", LUAF_unboxinteger);
+ lua_register(L, "boxboolean", LUAF_boxboolean);
+ lua_register(L, "unboxboolean", LUAF_unboxboolean);
+ lua_register(L, "onerror", LUAF_onerror);
+
+ lua_pushcfunction(L, LUAF_onerror);
+ luaL_loadfile(L, "puptest.lua");
+ lua_pcall(L,0,0,1);
+
+ lua_close(L);
+
+ return 0;
+}
diff --git a/engines/sword25/util/pluto/puptest.lua b/engines/sword25/util/pluto/puptest.lua
new file mode 100644
index 0000000000..e5ccdd64bd
--- /dev/null
+++ b/engines/sword25/util/pluto/puptest.lua
@@ -0,0 +1,93 @@
+-- $Id$
+
+require "pluto"
+
+permtable = { 1234 }
+
+perms = { [1] = coroutine.yield, [2] = permtable }
+
+function testcounter(counter)
+ local a = counter.cur()
+ counter.inc()
+ return counter.cur() == a+1
+end
+
+function testuvinthread(func)
+ local success, result = coroutine.resume(func)
+ return success and result == 5
+end
+
+
+function test(rootobj)
+ local passed = 0
+ local total = 0
+ local dotest = function(name,cond)
+ total = total+1
+ if cond then
+ print(name, " PASSED")
+ passed = passed + 1
+ else
+ print(name, "* FAILED")
+ end
+ end
+
+
+ dotest("Boolean FALSE ", rootobj.testfalse == false)
+ dotest("Boolean TRUE ", rootobj.testtrue == true)
+ dotest("Number 7 ", rootobj.testseven == 7)
+ dotest("String 'foobar' ", rootobj.testfoobar == "foobar")
+ dotest("Func returning 4 ", rootobj.testfuncreturnsfour() == 4)
+ dotest("Nil value ", rootobj.testnil == nil)
+ dotest("Thread resume ", coroutine.resume(rootobj.testthread) == true,14)
+ dotest("Table ", rootobj.testtbl.a == 2 and rootobj.testtbl[2] == 4);
+ dotest("Permanent table ", rootobj.testperm == permtable)
+ dotest("Table metatable ", rootobj.testmt() == 21)
+ dotest("Function env ", rootobj.testfenv() == 456)
+ dotest("Lua closure ", rootobj.testclosure() == 11)
+ dotest("Nil in closure ", rootobj.testnilclosure() == nil)
+ dotest("Nested func ", rootobj.testnest(1) == 6)
+ dotest("Light userdata ", checkludata(rootobj.testludata))
+ dotest("Looped tables ",
+ rootobj.testlooptable.testloopb.testloopa ==
+ rootobj.testlooptable)
+ dotest("Shared reference ", rootobj.testsharedrefa.sharedref ==
+ rootobj.testsharedrefb.sharedref)
+ dotest("Identical tables ", rootobj.testsharedrefa ~=
+ rootobj.testsharedrefb)
+ dotest("Table special persist", rootobj.testsptable.a == 6)
+ dotest("Udata literal persist",
+ unboxinteger(rootobj.testliteraludata) == 71)
+ dotest("Udata special persist",
+ unboxboolean(rootobj.testspudata1) == true and
+ unboxboolean(rootobj.testspudata2) == false)
+ dotest("Shared upvalues ",
+ testcounter(rootobj.testsharedupval))
+ dotest("Open upvalues ",
+ testuvinthread(rootobj.testuvinthread))
+ dotest("Upvalue cycles ",
+ rootobj.testuvcycle()[1] == rootobj.testuvcycle()[2])
+ dotest("__newindex metamethod", rootobj.testniinmt.a == 3)
+ dotest("Debug info ", (rootobj.testdebuginfo(2)) == "foo")
+ print()
+ if passed == total then
+ print("All tests passed.")
+ else
+ print(passed .. "/" .. total .. " tests passed.")
+ end
+end
+
+infile, err = io.open("test.plh", "rb")
+if infile == nil then
+ error("While opening: " .. (err or "no error"))
+end
+
+buf, err = infile:read("*a")
+if buf == nil then
+ error("While reading: " .. (err or "no error"))
+end
+
+infile:close()
+
+rootobj = pluto.unpersist(perms, buf)
+
+test(rootobj)
diff --git a/engines/testbed/config-params.cpp b/engines/testbed/config-params.cpp
new file mode 100644
index 0000000000..e5a581ec29
--- /dev/null
+++ b/engines/testbed/config-params.cpp
@@ -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$
+ */
+
+#include "common/config-manager.h"
+#include "common/fs.h"
+
+#include "testbed/config-params.h"
+
+DECLARE_SINGLETON(Testbed::ConfigParams)
+
+namespace Testbed {
+
+ConfigParams::ConfigParams() {
+ _logDirectory = "";
+ _logFilename = "";
+ _ws = 0;
+ _displayFont = Graphics::FontManager::kGUIFont;
+ _isInteractive = true;
+ _isGameDataFound = true;
+ _rerunTests = false;
+}
+
+void ConfigParams::initLogging(const char *dirname, const char *filename, bool enable) {
+ setLogDirectory(dirname);
+ setLogFilename(filename);
+ if (enable) {
+ _ws = Common::FSNode(_logDirectory).getChild(_logFilename).createWriteStream();
+ } else {
+ _ws = 0;
+ }
+}
+
+void ConfigParams::initLogging(bool enable) {
+ // Default Log Directory is game-data directory and filename is 'testbed.log'.
+ initLogging(ConfMan.get("path").c_str(), "testbed.log", enable);
+}
+
+bool ConfigParams::isRerunRequired() {
+ if (_rerunTests) {
+ _rerunTests = false;
+ return true;
+ }
+ return false;
+}
+
+void ConfigParams::deleteWriteStream() {
+ if (_ws) {
+ delete _ws;
+ }
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/config-params.h b/engines/testbed/config-params.h
new file mode 100644
index 0000000000..7a0e1cf5f2
--- /dev/null
+++ b/engines/testbed/config-params.h
@@ -0,0 +1,99 @@
+/* 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 TESTBED_CONFIG_PARAMS_H
+#define TESTBED_CONFIG_PARAMS_H
+
+#include "common/singleton.h"
+#include "common/stream.h"
+#include "graphics/fontman.h"
+
+class TestbedConfigManager;
+
+namespace Testbed {
+
+class ConfigParams : public Common::Singleton<ConfigParams> {
+private:
+ friend class Common::Singleton<SingletonBaseType>;
+ ConfigParams();
+
+ /**
+ * Private variables related to log files.
+ */
+ Common::String _logDirectory;
+ Common::String _logFilename;
+ Common::WriteStream *_ws;
+
+ /**
+ * Private variable used for font.
+ */
+ Graphics::FontManager::FontUsage _displayFont;
+
+ /**
+ * Determines if the user initiated testing session is interactive or not.
+ * Used by various tests to respond accordingly.
+ */
+ bool _isInteractive;
+ bool _isGameDataFound;
+ bool _rerunTests;
+ TestbedConfigManager *_testbedConfMan;
+
+public:
+
+ bool isRerunRequired();
+ void setRerunFlag(bool flag) { _rerunTests = flag; }
+
+ bool isSessionInteractive() { return _isInteractive; }
+ void setSessionAsInteractive(bool status) { _isInteractive = status; }
+
+ bool isGameDataFound() { return _isGameDataFound; }
+ void setGameDataFound(bool status) { _isGameDataFound = status; }
+
+ TestbedConfigManager *getTestbedConfigManager() { return _testbedConfMan; }
+ void setTestbedConfigManager(TestbedConfigManager* confMan) { _testbedConfMan = confMan; }
+
+ Common::String &getLogDirectory() { return _logDirectory; }
+ void setLogDirectory(const Common::String &dirname) { _logDirectory = dirname; }
+ Common::String &getLogFilename() { return _logFilename; }
+ void setLogFilename(const Common::String &filename) { _logFilename = filename; }
+
+ Common::WriteStream *getLogWriteStream() { return _ws; }
+ Graphics::FontManager::FontUsage getCurrentFontUsageType() { return _displayFont; }
+ void setCurrentFontUsageType(Graphics::FontManager::FontUsage f) { _displayFont = f; }
+
+ /**
+ * Note: To enable logging, this function must be called once first.
+ */
+ void initLogging(const char *dirname, const char *filename, bool enable = true);
+ void initLogging(bool enable = true);
+
+ void deleteWriteStream();
+};
+
+/** Shortcut for accessing ConfigParams */
+#define ConfParams ConfigParams::instance()
+
+} // End of Namespace Testbed
+
+#endif
diff --git a/engines/testbed/config.cpp b/engines/testbed/config.cpp
new file mode 100644
index 0000000000..fe34910204
--- /dev/null
+++ b/engines/testbed/config.cpp
@@ -0,0 +1,308 @@
+/* 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/stream.h"
+#include "common/config-manager.h"
+
+#include "engines/engine.h"
+
+#include "testbed/config.h"
+#include "testbed/fs.h"
+
+namespace Testbed {
+
+TestbedOptionsDialog::TestbedOptionsDialog(Common::Array<Testsuite *> &tsList, TestbedConfigManager *tsConfMan) : GUI::Dialog("Browser"), _testbedConfMan(tsConfMan) {
+
+ new GUI::StaticTextWidget(this, "Browser.Headline", "Select Testsuites to Execute");
+ new GUI::StaticTextWidget(this, "Browser.Path", "Use Doubleclick to select/deselect");
+
+ // Construct a String Array
+ Common::Array<Testsuite *>::const_iterator iter;
+ Common::String description;
+ uint selected = 0;
+
+ for (iter = tsList.begin(); iter != tsList.end(); iter++) {
+ _testSuiteArray.push_back(*iter);
+ description = (*iter)->getDescription();
+ if ((*iter)->isEnabled()) {
+ _testSuiteDescArray.push_back(description + "(selected)");
+ selected++;
+ _colors.push_back(GUI::ThemeEngine::kFontColorNormal);
+ } else {
+ _testSuiteDescArray.push_back(description);
+ _colors.push_back(GUI::ThemeEngine::kFontColorAlternate);
+ }
+ }
+
+ _testListDisplay = new TestbedListWidget(this, "Browser.List", _testSuiteArray);
+ _testListDisplay->setNumberingMode(GUI::kListNumberingOff);
+ _testListDisplay->setList(_testSuiteDescArray, &_colors);
+
+ // This list shouldn't be editable
+ _testListDisplay->setEditable(false);
+
+ if (selected > (tsList.size() - selected)) {
+ _selectButton = new GUI::ButtonWidget(this, "Browser.Up", "Deselect All", 0, kTestbedDeselectAll, 0);
+ } else {
+ _selectButton = new GUI::ButtonWidget(this, "Browser.Up", "Select All", 0, kTestbedSelectAll, 0);
+ }
+ new GUI::ButtonWidget(this, "Browser.Cancel", "Run tests", 0, GUI::kCloseCmd);
+ new GUI::ButtonWidget(this, "Browser.Choose", "Exit Testbed", 0, kTestbedQuitCmd);
+}
+
+TestbedOptionsDialog::~TestbedOptionsDialog() {}
+
+void TestbedOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ Testsuite *ts;
+ Common::WriteStream *ws;
+ switch (cmd) {
+ case GUI::kListItemDoubleClickedCmd:
+ ts = _testSuiteArray[_testListDisplay->getSelected()];
+ if (ts) {
+ // Toggle status
+ if (ts->isEnabled()) {
+ ts->enable(false);
+ } else {
+ ts->enable(true);
+ }
+
+ // Now render status
+ if (ts->isEnabled()) {
+ _testListDisplay->markAsSelected(_testListDisplay->getSelected());
+ } else {
+ _testListDisplay->markAsDeselected(_testListDisplay->getSelected());
+ }
+ }
+ break;
+
+ case kTestbedQuitCmd:
+ Engine::quitGame();
+ close();
+ break;
+
+ case kTestbedDeselectAll:
+ _selectButton->setLabel("Select All");
+ _selectButton->setCmd(kTestbedSelectAll);
+ for (uint i = 0; i < _testSuiteArray.size(); i++) {
+ _testListDisplay->markAsDeselected(i);
+ ts = _testSuiteArray[i];
+ if (ts) {
+ ts->enable(false);
+ }
+ }
+ break;
+
+ case kTestbedSelectAll:
+ _selectButton->setLabel("Deselect All");
+ _selectButton->setCmd(kTestbedDeselectAll);
+ for (uint i = 0; i < _testSuiteArray.size(); i++) {
+ _testListDisplay->markAsSelected(i);
+ ts = _testSuiteArray[i];
+ if (ts) {
+ ts->enable(true);
+ }
+ }
+ break;
+ case GUI::kCloseCmd:
+ // This is final selected state, write it to config file.
+ ws = _testbedConfMan->getConfigWriteStream();
+ _testbedConfMan->writeTestbedConfigToStream(ws);
+ delete ws;
+ default:
+ GUI::Dialog::handleCommand(sender, cmd, data);
+
+ }
+}
+
+void TestbedInteractionDialog::addText(uint w, uint h, const Common::String text, Graphics::TextAlign textAlign, uint xOffset, uint yPadding) {
+ if (!xOffset) {
+ xOffset = _xOffset;
+ }
+ _yOffset += yPadding;
+ new GUI::StaticTextWidget(this, xOffset, _yOffset, w, h, text, textAlign);
+ _yOffset += h;
+}
+
+void TestbedInteractionDialog::addButton(uint w, uint h, const Common::String name, uint32 cmd, uint xOffset, uint yPadding) {
+ if (!xOffset) {
+ xOffset = _xOffset;
+ }
+ _yOffset += yPadding;
+ _buttonArray.push_back(new GUI::ButtonWidget(this, xOffset, _yOffset, w, h, name, 0, cmd));
+ _yOffset += h;
+}
+
+void TestbedInteractionDialog::addList(uint x, uint y, uint w, uint h, Common::Array<Common::String> &strArray, GUI::ListWidget::ColorList *colors, uint yPadding) {
+ _yOffset += yPadding;
+ GUI::ListWidget *list = new GUI::ListWidget(this, x, y, w, h);
+ list->setEditable(false);
+ list->setNumberingMode(GUI::kListNumberingOff);
+ list->setList(strArray, colors);
+ _yOffset += h;
+}
+
+void TestbedInteractionDialog::addButtonXY(uint x, uint y, uint w, uint h, const Common::String name, uint32 cmd) {
+ _buttonArray.push_back(new GUI::ButtonWidget(this, x, _yOffset, w, h, name, 0, cmd));
+}
+
+void TestbedInteractionDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ GUI::Dialog::handleCommand(sender, cmd, data);
+}
+
+void TestbedConfigManager::initDefaultConfiguration() {
+ // Default Configuration
+ // Add Global configuration Parameters here.
+ _configFileInterface.setKey("isSessionInteractive", "Global", "true");
+}
+
+void TestbedConfigManager::writeTestbedConfigToStream(Common::WriteStream *ws) {
+ Common::String wStr;
+ for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i < _testsuiteList.end(); i++) {
+ _configFileInterface.setKey("this", (*i)->getName(), boolToString((*i)->isEnabled()));
+ const Common::Array<Test *> &testList = (*i)->getTestList();
+ for (Common::Array<Test *>::const_iterator j = testList.begin(); j != testList.end(); j++) {
+ _configFileInterface.setKey((*j)->featureName, (*i)->getName(), boolToString((*j)->enabled));
+ }
+ }
+ _configFileInterface.saveToStream(*ws);
+ ws->flush();
+}
+
+Common::SeekableReadStream *TestbedConfigManager::getConfigReadStream() {
+ // Look for config file using SearchMan
+ Common::SeekableReadStream *rs = SearchMan.createReadStreamForMember(_configFileName);
+ return rs;
+}
+
+Common::WriteStream *TestbedConfigManager::getConfigWriteStream() {
+ // Look for config file in game-path
+ const Common::String &path = ConfMan.get("path");
+ Common::WriteStream *ws;
+ Common::FSNode gameRoot(path);
+ Common::FSNode config = gameRoot.getChild(_configFileName);
+ ws = config.createWriteStream();
+ return ws;
+}
+
+void TestbedConfigManager::parseConfigFile() {
+ Common::SeekableReadStream *rs = getConfigReadStream();
+
+ if (!rs) {
+ Testsuite::logPrintf("Info! No config file found, using default configuration.\n");
+ initDefaultConfiguration();
+ return;
+ }
+ _configFileInterface.loadFromStream(*rs);
+ Common::ConfigFile::SectionList sections = _configFileInterface.getSections();
+ Testsuite *currTS = 0;
+
+ for (Common::ConfigFile::SectionList::const_iterator i = sections.begin(); i != sections.end(); i++) {
+ if (i->name.equalsIgnoreCase("Global")) {
+ // Global params may be directly queried, ignore them
+ } else {
+ // A testsuite, process it.
+ currTS = getTestsuiteByName(i->name);
+ Common::ConfigFile::SectionKeyList kList = i->getKeys();
+ if (!currTS) {
+ Testsuite::logPrintf("Warning! Error in config: Testsuite %s not found\n", i->name.c_str());
+ continue;
+ }
+
+ for (Common::ConfigFile::SectionKeyList::const_iterator j = kList.begin(); j != kList.end(); j++) {
+ if (j->key.equalsIgnoreCase("this")) {
+ currTS->enable(stringToBool(j->value));
+ } else {
+ if (!currTS->enableTest(j->key, stringToBool(j->value))) {
+ Testsuite::logPrintf("Warning! Error in config: Test %s not found\n", j->key.c_str());
+ }
+ }
+ }
+ }
+ }
+ delete rs;
+}
+
+int TestbedConfigManager::getNumSuitesEnabled() {
+ int count = 0;
+ for (uint i = 0; i < _testsuiteList.size(); i++) {
+ if (_testsuiteList[i]->isEnabled()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+Testsuite *TestbedConfigManager::getTestsuiteByName(const Common::String &name) {
+ for (uint i = 0; i < _testsuiteList.size(); i++) {
+ if (name.equalsIgnoreCase(_testsuiteList[i]->getName())) {
+ return _testsuiteList[i];
+ }
+ }
+ return 0;
+}
+
+void TestbedConfigManager::selectTestsuites() {
+
+ parseConfigFile();
+
+ if (_configFileInterface.hasKey("isSessionInteractive", "Global")) {
+ Common::String in;
+ _configFileInterface.getKey("isSessionInteractive", "Global", in);
+ ConfParams.setSessionAsInteractive(stringToBool(in));
+ }
+
+ if (!ConfParams.isSessionInteractive()) {
+ // Non interactive sessions don't need to go beyond
+ return;
+ }
+
+ // XXX: disabling these as of now for fastly testing other tests
+ // Testsuite::isSessionInteractive = false;
+ Common::String prompt("Welcome to the ScummVM testbed!\n"
+ "It is a framework to test the various ScummVM subsystems namely GFX, Sound, FS, events etc.\n"
+ "If you see this, it means interactive tests would run on this system :)\n");
+
+ if (!ConfParams.isGameDataFound()) {
+ prompt += "\nSeems like Game data files are not configured properly.\n"
+ "Create Game data files using script ./create-testbed-data.sh in dists/engine-data\n"
+ "Next, Configure the game path in launcher / command-line.\n"
+ "Currently a few testsuites namely FS/AudioCD/MIDI would be disabled\n";
+ }
+
+ Testsuite::logPrintf("Info! : Interactive tests are also being executed.\n");
+
+ if (Testsuite::handleInteractiveInput(prompt, "Proceed?", "Customize", kOptionRight)) {
+ if (Engine::shouldQuit()) {
+ return;
+ }
+ // Select testsuites using checkboxes
+ TestbedOptionsDialog tbd(_testsuiteList, this);
+ tbd.runModal();
+ }
+
+ // Clear it to remove entries before next rerun
+ _configFileInterface.clear();
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/config.h b/engines/testbed/config.h
new file mode 100644
index 0000000000..2ee5b09002
--- /dev/null
+++ b/engines/testbed/config.h
@@ -0,0 +1,135 @@
+/* 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 TESTBED_CONFIG_H
+#define TESTBED_CONFIG_H
+
+
+#include "common/array.h"
+#include "common/config-file.h"
+#include "common/str-array.h"
+#include "common/tokenizer.h"
+
+#include "gui/ListWidget.h"
+#include "gui/options.h"
+#include "gui/ThemeEngine.h"
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+enum {
+ kTestbedQuitCmd = 'Quit',
+ kTestbedSelectAll = 'sAll',
+ kTestbedDeselectAll = 'dAll'
+};
+
+
+
+class TestbedConfigManager {
+public:
+ TestbedConfigManager(Common::Array<Testsuite *> &tList, const Common::String fName) : _testsuiteList(tList), _configFileName(fName) {}
+ ~TestbedConfigManager() {}
+ void selectTestsuites();
+ void setConfigFile(const Common::String fName) { _configFileName = fName; }
+ Common::SeekableReadStream *getConfigReadStream();
+ Common::WriteStream *getConfigWriteStream();
+ void writeTestbedConfigToStream(Common::WriteStream *ws);
+ Testsuite *getTestsuiteByName(const Common::String &name);
+ bool stringToBool(const Common::String str) { return str.equalsIgnoreCase("true") ? true : false; }
+ Common::String boolToString(bool val) { return val ? "true" : "false"; }
+ void initDefaultConfiguration();
+ int getNumSuitesEnabled();
+
+private:
+ Common::Array<Testsuite *> &_testsuiteList;
+ Common::String _configFileName;
+ Common::ConfigFile _configFileInterface;
+ void parseConfigFile();
+};
+
+class TestbedListWidget : public GUI::ListWidget {
+public:
+ TestbedListWidget(GUI::Dialog *boss, const Common::String &name, Common::Array<Testsuite *> tsArray) : GUI::ListWidget(boss, name), _testSuiteArray(tsArray) {}
+
+ void markAsSelected(int i) {
+ if (!_list[i].contains("selected")) {
+ _list[i] += " (selected)";
+ }
+ _listColors[i] = GUI::ThemeEngine::kFontColorNormal;
+ draw();
+ }
+
+ void markAsDeselected(int i) {
+ if (_list[i].contains("selected")) {
+ _list[i] = _testSuiteArray[i]->getDescription();
+ }
+ _listColors[i] = GUI::ThemeEngine::kFontColorAlternate;
+ draw();
+ }
+
+ void setColor(uint32 indx, GUI::ThemeEngine::FontColor color) {
+ assert(indx < _listColors.size());
+ _listColors[indx] = color;
+ draw();
+ }
+
+private:
+ Common::Array<Testsuite *> _testSuiteArray;
+};
+
+class TestbedOptionsDialog : public GUI::Dialog {
+public:
+ TestbedOptionsDialog(Common::Array<Testsuite *> &tsList, TestbedConfigManager *tsConfMan);
+ ~TestbedOptionsDialog();
+ void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+private:
+ GUI::ListWidget::ColorList _colors;
+ GUI::ButtonWidget *_selectButton;
+ Common::Array<Testsuite *> _testSuiteArray;
+ Common::StringArray _testSuiteDescArray;
+ TestbedListWidget *_testListDisplay;
+ TestbedConfigManager *_testbedConfMan;
+};
+
+class TestbedInteractionDialog : public GUI::Dialog {
+public:
+ TestbedInteractionDialog(uint x, uint y, uint w, uint h) : GUI::Dialog(x, y, w, h) {}
+ ~TestbedInteractionDialog() {}
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ void addButton(uint w, uint h, const Common::String name, uint32 cmd, uint xOffset = 0, uint yPadding = 8);
+ void addButtonXY(uint x, uint y, uint w, uint h, const Common::String name, uint32 cmd);
+ void addText(uint w, uint h, const Common::String text, Graphics::TextAlign textAlign, uint xOffset, uint yPadding = 8);
+ void addList(uint x, uint y, uint w, uint h, Common::Array<Common::String> &strArray, GUI::ListWidget::ColorList *colors = 0, uint yPadding = 8);
+protected:
+ Common::Array<GUI::ButtonWidget *> _buttonArray;
+ uint _xOffset;
+ uint _yOffset;
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_CONFIG_H
diff --git a/engines/testbed/detection.cpp b/engines/testbed/detection.cpp
new file mode 100644
index 0000000000..1b8a86cea6
--- /dev/null
+++ b/engines/testbed/detection.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 "common/config-manager.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "common/fs.h"
+
+#include "base/plugins.h"
+
+#include "testbed/testbed.h"
+
+static const PlainGameDescriptor testbed_setting[] = {
+ { "testbed", "Testbed: The Backend Testing Framework" },
+ { 0, 0 }
+};
+
+static const ADGameDescription testbedDescriptions[] = {
+ {
+ "testbed",
+ "",
+ AD_ENTRY1("TESTBED", 0), // Game-data file for detection
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ AD_TABLE_END_MARKER
+};
+
+static const ADParams detectionParams = {
+ (const byte *)testbedDescriptions,
+ sizeof(ADGameDescription),
+ 512,
+ testbed_setting,
+ 0,
+ "testbed",
+ 0,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE,
+ 1,
+ 0
+};
+
+class TestbedMetaEngine : public AdvancedMetaEngine {
+public:
+ TestbedMetaEngine() : AdvancedMetaEngine(detectionParams) {
+ }
+
+ virtual const char *getName() const {
+ return "TestBed: The Backend Testing Framework";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Copyright (C) ScummVM";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ // Instantiate Engine even if the game data is not found.
+ *engine = new Testbed::TestbedEngine(syst);
+ return true;
+ }
+
+};
+
+#if PLUGIN_ENABLED_DYNAMIC(TESTBED)
+ REGISTER_PLUGIN_DYNAMIC(TESTBED, PLUGIN_TYPE_ENGINE, TestbedMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(TESTBED, PLUGIN_TYPE_ENGINE, TestbedMetaEngine);
+#endif
diff --git a/engines/testbed/events.cpp b/engines/testbed/events.cpp
new file mode 100644
index 0000000000..b0a930172d
--- /dev/null
+++ b/engines/testbed/events.cpp
@@ -0,0 +1,314 @@
+/* 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/events.h"
+#include "common/keyboard.h"
+
+#include "engines/engine.h"
+
+#include "graphics/cursorman.h"
+
+#include "testbed/events.h"
+#include "testbed/graphics.h"
+
+namespace Testbed {
+
+struct keycodeToChar {
+ Common::KeyCode code;
+ char value;
+} keyCodeLUT[] = {
+ {Common::KEYCODE_a, 'a'},
+ {Common::KEYCODE_b, 'b'},
+ {Common::KEYCODE_c, 'c'},
+ {Common::KEYCODE_d, 'd'},
+ {Common::KEYCODE_e, 'e'},
+ {Common::KEYCODE_f, 'f'},
+ {Common::KEYCODE_g, 'g'},
+ {Common::KEYCODE_h, 'h'},
+ {Common::KEYCODE_i, 'i'},
+ {Common::KEYCODE_j, 'j'},
+ {Common::KEYCODE_k, 'k'},
+ {Common::KEYCODE_l, 'l'},
+ {Common::KEYCODE_m, 'm'},
+ {Common::KEYCODE_n, 'n'},
+ {Common::KEYCODE_o, 'o'},
+ {Common::KEYCODE_p, 'p'},
+ {Common::KEYCODE_q, 'q'},
+ {Common::KEYCODE_r, 'r'},
+ {Common::KEYCODE_s, 's'},
+ {Common::KEYCODE_t, 't'},
+ {Common::KEYCODE_u, 'u'},
+ {Common::KEYCODE_v, 'v'},
+ {Common::KEYCODE_w, 'w'},
+ {Common::KEYCODE_x, 'x'},
+ {Common::KEYCODE_y, 'y'},
+ {Common::KEYCODE_z, 'z'},
+ {Common::KEYCODE_0, '0'},
+ {Common::KEYCODE_1, '1'},
+ {Common::KEYCODE_2, '2'},
+ {Common::KEYCODE_3, '3'},
+ {Common::KEYCODE_4, '4'},
+ {Common::KEYCODE_5, '5'},
+ {Common::KEYCODE_6, '6'},
+ {Common::KEYCODE_7, '7'},
+ {Common::KEYCODE_8, '8'},
+ {Common::KEYCODE_9, '9'},
+ {Common::KEYCODE_SPACE, ' '}
+};
+
+char EventTests::keystrokeToChar() {
+ Common::EventManager *eventMan = g_system->getEventManager();
+ bool quitLoop = false;
+ Common::Event event;
+
+ // handle all keybd events
+ while (!quitLoop) {
+ while (eventMan->pollEvent(event)) {
+ // Quit if explicitly requested!
+ if (Engine::shouldQuit()) {
+ return 0;
+ }
+
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
+ return 0;
+ }
+ for (int i = 0; i < ARRAYSIZE(keyCodeLUT); i++) {
+ if (event.kbd.keycode == keyCodeLUT[i].code) {
+ return keyCodeLUT[i].value;
+ }
+ }
+ break;
+ default:
+ break; // Ignore other events
+ }
+ }
+ }
+
+ return 0;
+}
+
+Common::Rect EventTests::drawFinishZone() {
+ Graphics::Surface *screen = g_system->lockScreen();
+ const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont));
+ int width = 35;
+ int height = 20;
+ int right = g_system->getWidth();
+ Common::Rect rect(0, 0, right, height);
+ Common::Rect rect2(0, 0, right - width, height);
+ screen->fillRect(rect, kColorSpecial);
+ screen->fillRect(rect2, kColorBlack);
+ g_system->unlockScreen();
+ font.drawString(screen, "Close", rect.left, rect.top, screen->w, kColorBlack, Graphics::kTextAlignRight);
+ g_system->updateScreen();
+ return Common::Rect(right - width, 0, right, height);
+}
+
+TestExitStatus EventTests::mouseEvents() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing Mouse events.\n "
+ "Any movement/click generated by L/R/M mouse buttons or the mouse wheel should be detected.\n"
+ "Press X to exit";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : keyboard events\n");
+ return kTestSkipped;
+ }
+
+ Common::EventManager *eventMan = g_system->getEventManager();
+
+ Common::Point pt(0, 30);
+ Common::Rect rectInfo = Testsuite::writeOnScreen("Generate mouse events make L/R/M button clicks, move wheel", pt);
+ pt.y += 15;
+ Testsuite::writeOnScreen("Press X to exit", pt);
+ pt.y = 70;
+ Common::Rect rectLB = Testsuite::writeOnScreen("Left-button click : Not tested", pt);
+ pt.y += 15;
+ Common::Rect rectRB = Testsuite::writeOnScreen("Right-button click : Not tested", pt);
+ pt.y += 15;
+ Common::Rect rectMB = Testsuite::writeOnScreen("Middle-button click : Not tested", pt);
+ pt.y += 15;
+ Common::Rect rectWheel = Testsuite::writeOnScreen("Wheel Movements : Not tested", pt);
+
+
+ // Init Mouse Palette
+ GFXtests::initMousePalette();
+ Common::Rect finishZone = drawFinishZone();
+
+ bool quitLoop = false;
+ TestExitStatus passed = kTestPassed;
+ // handle all mouse events
+ Common::Event event;
+ while (!quitLoop) {
+ // Show mouse
+ CursorMan.showMouse(true);
+ g_system->updateScreen();
+
+ while (eventMan->pollEvent(event)) {
+ // Quit if explicitly requested
+ if (Engine::shouldQuit()) {
+ return passed;
+ }
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ // Movements havee already been tested in GFX
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse left-button pressed", Common::Point(rectInfo.left, rectInfo.top));
+ break;
+ case Common::EVENT_RBUTTONDOWN:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse right-button pressed", Common::Point(rectInfo.left, rectInfo.top));
+ break;
+ case Common::EVENT_WHEELDOWN:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse wheel moved down", Common::Point(rectInfo.left, rectInfo.top));
+ Testsuite::writeOnScreen("Wheel Movements : Done!", Common::Point(rectWheel.left, rectWheel.top));
+ break;
+ case Common::EVENT_MBUTTONDOWN:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse middle-button pressed ", Common::Point(rectInfo.left, rectInfo.top));
+ break;
+ case Common::EVENT_LBUTTONUP:
+ Testsuite::clearScreen(rectInfo);
+ if (finishZone.contains(eventMan->getMousePos())) {
+ quitLoop = true;
+ }
+ Testsuite::writeOnScreen("Mouse left-button released", Common::Point(rectInfo.left, rectInfo.top));
+ Testsuite::writeOnScreen("Left-button clicks : Done!", Common::Point(rectLB.left, rectLB.top));
+ break;
+ case Common::EVENT_RBUTTONUP:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse right-button released", Common::Point(rectInfo.left, rectInfo.top));
+ Testsuite::writeOnScreen("Right-button clicks : Done!", Common::Point(rectRB.left, rectRB.top));
+ break;
+ case Common::EVENT_WHEELUP:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse wheel moved up", Common::Point(rectInfo.left, rectInfo.top));
+ Testsuite::writeOnScreen("Wheel Movements : Done!", Common::Point(rectWheel.left, rectWheel.top));
+ break;
+ case Common::EVENT_MBUTTONUP:
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Mouse middle-button released ", Common::Point(rectInfo.left, rectInfo.top));
+ Testsuite::writeOnScreen("Middle-button clicks : Done!", Common::Point(rectMB.left, rectMB.top));
+ break;
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_x) {
+ Testsuite::clearScreen(rectInfo);
+ Testsuite::writeOnScreen("Exit requested", Common::Point(rectInfo.left, rectInfo.top));
+ quitLoop = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+
+ CursorMan.showMouse(false);
+
+ // Verify results now!
+ if (Testsuite::handleInteractiveInput("Were mouse clicks (L/R/M buttons) and wheel movements identfied ?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Mouse clicks (L/R/M buttons) and wheel movements failed");
+ passed = kTestFailed;
+ }
+
+ return passed;
+}
+
+TestExitStatus EventTests::kbdEvents() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing keyboard events.\n "
+ "Testbed should be able to figure out any alphanumeric keystrokes made by the user and display them back.\n"
+ "Press ESC key when done of the input.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : keyboard events\n");
+ return kTestSkipped;
+ }
+
+
+ // Make user type some word and display the output on screen
+ Common::String text = "You Entered : ";
+ Common::Point pt(0, 100);
+ Testsuite::clearScreen();
+ Testsuite::writeOnScreen("Enter your word, press ESC when done, it will be echoed back", pt);
+ pt.y += 20;
+ Common::Rect rect = Testsuite::writeOnScreen(text, pt);
+ char letter;
+ while ((letter = keystrokeToChar()) != 0) {
+ Testsuite::clearScreen(rect);
+ text += letter;
+ rect = Testsuite::writeOnScreen(text, pt);
+ }
+
+ TestExitStatus passed = kTestPassed;
+
+ if (Testsuite::handleInteractiveInput("Was the word you entered same as that displayed on screen?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Keyboard Events failed");
+ passed = kTestFailed;
+ }
+
+ Testsuite::clearScreen();
+ return passed;
+}
+
+TestExitStatus EventTests::showMainMenu() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing Main Menu events.\n "
+ "Main Menu event is normally trigerred by user pressing (Ctrl + f5).\n"
+ "Click 'resume'(the topmost button) to continue testbed.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Main Menu\n");
+ return kTestSkipped;
+ }
+ Common::EventManager *eventMan = g_system->getEventManager();
+ Common::Event mainMenuEvent;
+ mainMenuEvent.type = Common::EVENT_MAINMENU;
+ eventMan->pushEvent(mainMenuEvent);
+
+ TestExitStatus passed = kTestPassed;
+
+ if (Testsuite::handleInteractiveInput("Were you able to see a main menu widget?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Event MAINMENU failed");
+ passed = kTestFailed;
+ }
+
+ return passed;
+}
+
+EventTestSuite::EventTestSuite() {
+ addTest("MouseEvents", &EventTests::mouseEvents);
+ addTest("KeyboardEvents", &EventTests::kbdEvents);
+ addTest("MainmenuEvent", &EventTests::showMainMenu);
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/events.h b/engines/testbed/events.h
new file mode 100644
index 0000000000..607bba79d5
--- /dev/null
+++ b/engines/testbed/events.h
@@ -0,0 +1,67 @@
+/* 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 TESTBED_EVENTS_H
+#define TESTBED_EVENTS_H
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+namespace EventTests {
+
+// Helper functions for Event tests
+char keystrokeToChar();
+Common::Rect drawFinishZone();
+// will contain function declarations for Event tests
+TestExitStatus mouseEvents();
+TestExitStatus kbdEvents();
+TestExitStatus showMainMenu();
+// add more here
+
+} // End of namespace EventTests
+
+class EventTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the EventTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ EventTestSuite();
+ ~EventTestSuite() {}
+ const char *getName() const {
+ return "Events";
+ }
+ const char *getDescription() const {
+ return "Events : Keyboard/Mouse/RTL";
+ }
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_EVENTS_H
diff --git a/engines/testbed/fs.cpp b/engines/testbed/fs.cpp
new file mode 100644
index 0000000000..f951224910
--- /dev/null
+++ b/engines/testbed/fs.cpp
@@ -0,0 +1,198 @@
+/* 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/stream.h"
+#include "common/util.h"
+
+#include "testbed/fs.h"
+
+namespace Testbed {
+
+/**
+ * This test does the following:
+ * 1) acquires the game-data path
+ * 2) In the game-root it navigates to "directory" and opens the file "file"
+ *
+ * The code accesses the appropriate file using the fileSystem API, creates a read stream of it and
+ * compares the message contained in it, with what it expects.
+ *
+ */
+bool FStests::readDataFromFile(Common::FSDirectory *directory, const char *file) {
+
+ Common::SeekableReadStream *readStream = directory->createReadStreamForMember(file);
+
+ if (!readStream) {
+ Testsuite::logDetailedPrintf("Can't open game file for reading\n");
+ return false;
+ }
+
+ Common::String msg = readStream->readLine();
+ delete readStream;
+
+ Testsuite::logDetailedPrintf("Message Extracted from %s/%s : %s\n", directory->getFSNode().getName().c_str(), file, msg.c_str());
+
+ Common::String expectedMsg = "It works!";
+
+ if (!msg.equals(expectedMsg)) {
+ Testsuite::logDetailedPrintf("Can't read Correct data from file\n");
+ return false;
+ }
+
+ return true;
+}
+
+TestExitStatus FStests::testReadFile() {
+ const Common::String &path = ConfMan.get("path");
+ Common::FSDirectory gameRoot(path);
+ int numFailed = 0;
+
+ if (!gameRoot.getFSNode().exists() || !gameRoot.getFSNode().isDirectory()) {
+ Testsuite::logDetailedPrintf("game Path should be an existing directory");
+ return kTestFailed;
+ }
+
+ const char *dirList[] = {"test1" ,"Test2", "TEST3" , "tEST4", "test5"};
+ const char *file[] = {"file.txt", "File.txt", "FILE.txt", "fILe.txt", "file"};
+
+ for (unsigned int i = 0; i < ARRAYSIZE(dirList); i++) {
+ Common::String dirName = dirList[i];
+ Common::String fileName = file[i];
+ Common::FSDirectory *directory = gameRoot.getSubDirectory(dirName);
+
+ if (!directory) {
+ Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
+ return kTestFailed;
+ }
+
+ if (!readDataFromFile(directory, fileName.c_str())) {
+ Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
+ numFailed++;
+ }
+
+ dirName.toLowercase();
+ fileName.toLowercase();
+ delete directory;
+ directory = gameRoot.getSubDirectory(dirName);
+
+ if (!directory) {
+ Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
+ return kTestFailed;
+ }
+
+ if (!readDataFromFile(directory, fileName.c_str())) {
+ Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
+ numFailed++;
+ }
+
+ dirName.toUppercase();
+ fileName.toUppercase();
+ delete directory;
+ directory = gameRoot.getSubDirectory(dirName);
+
+ if (!directory) {
+ Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
+ return kTestFailed;
+ }
+
+ if (!readDataFromFile(directory, fileName.c_str())) {
+ Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
+ numFailed++;
+ }
+ delete directory;
+ }
+
+ Testsuite::logDetailedPrintf("Failed %d out of 15\n", numFailed);
+ if (numFailed) {
+ return kTestFailed;
+ } else {
+ return kTestPassed;
+ }
+}
+
+/**
+ * This test creates a file testbed.out, writes a sample data and confirms if
+ * it is same by reading the file again.
+ */
+TestExitStatus FStests::testWriteFile() {
+ const Common::String &path = ConfMan.get("path");
+ Common::FSNode gameRoot(path);
+ if (!gameRoot.exists()) {
+ Testsuite::logPrintf("Couldn't open the game data directory %s", path.c_str());
+ return kTestFailed;
+ }
+
+ Common::FSNode fileToWrite = gameRoot.getChild("testbed.out");
+
+ Common::WriteStream *ws = fileToWrite.createWriteStream();
+
+ if (!ws) {
+ Testsuite::logDetailedPrintf("Can't open writable file in game data dir\n");
+ return kTestFailed;
+ }
+
+ ws->writeString("ScummVM Rocks!");
+ ws->flush();
+ delete ws;
+
+ Common::SeekableReadStream *rs = fileToWrite.createReadStream();
+ if (!rs) {
+ Testsuite::logDetailedPrintf("Can't open recently written file testbed.out in game data dir\n");
+ return kTestFailed;
+ }
+ Common::String readFromFile = rs->readLine();
+ delete rs;
+
+ if (readFromFile.equals("ScummVM Rocks!")) {
+ // All good
+ Testsuite::logDetailedPrintf("Data written and read correctly\n");
+ return kTestPassed;
+ }
+
+ return kTestFailed;
+}
+
+
+
+FSTestSuite::FSTestSuite() {
+ // FS tests depend on Game Data files.
+ // If those are not found. Disable this testsuite.
+ const Common::String &path = ConfMan.get("path");
+ Common::FSNode gameRoot(path);
+
+ Common::FSNode gameIdentificationFile = gameRoot.getChild("TESTBED");
+ if (!gameIdentificationFile.exists()) {
+ logPrintf("WARNING! : Game Data not found. Skipping FS tests\n");
+ ConfParams.setGameDataFound(false);
+ Testsuite::enable(false);
+ }
+ addTest("ReadingFile", &FStests::testReadFile, false);
+ addTest("WritingFile", &FStests::testWriteFile, false);
+}
+
+void FSTestSuite::enable(bool flag) {
+ Testsuite::enable(ConfParams.isGameDataFound() & flag);
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/fs.h b/engines/testbed/fs.h
new file mode 100644
index 0000000000..c51d898c9d
--- /dev/null
+++ b/engines/testbed/fs.h
@@ -0,0 +1,74 @@
+/* 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 TESTBED_FS_H
+#define TESTBED_FS_H
+
+#include "common/fs.h"
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+namespace FStests {
+
+// Note: These tests require a game-data directory
+// So would work if game-path is set in the launcher or invoked as ./scummvm --path="path-to-testbed-data" testbed
+// from commandline
+
+// Helper functions for FS tests
+bool readDataFromFile(Common::FSDirectory *directory, const char *file);
+
+// will contain function declarations for FS tests
+TestExitStatus testReadFile();
+TestExitStatus testWriteFile();
+TestExitStatus testOpeningSaveFile();
+// add more here
+
+} // End of namespace FStests
+
+class FSTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the FSTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ FSTestSuite();
+ ~FSTestSuite() {}
+ const char *getName() const {
+ return "FS";
+ }
+ const char *getDescription() const {
+ return "File system tests (Navigation, Read/Write)";
+ }
+ void enable(bool flag);
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_FS_H
diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp
new file mode 100644
index 0000000000..086db21c67
--- /dev/null
+++ b/engines/testbed/graphics.cpp
@@ -0,0 +1,1150 @@
+/* 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/events.h"
+#include "common/list.h"
+#include "common/random.h"
+
+#include "engines/engine.h"
+
+#include "testbed/graphics.h"
+#include "testbed/testsuite.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/fontman.h"
+#include "graphics/surface.h"
+#include "graphics/VectorRendererSpec.h"
+
+namespace Testbed {
+
+byte GFXTestSuite::_palette[256 * 4] = {0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0};
+
+GFXTestSuite::GFXTestSuite() {
+ // Initialize color palettes
+ // The fourth field is for alpha channel which is unused
+ // Assuming 8bpp as of now
+ g_system->setPalette(_palette, 0, 3);
+
+ // Init Mouse Palette (White-black-yellow)
+ GFXtests::initMousePalette();
+
+ // Add tests here
+
+ // Blitting buffer on screen
+ addTest("BlitBitmaps", &GFXtests::copyRectToScreen);
+
+ // GFX Transcations
+ addTest("FullScreenMode", &GFXtests::fullScreenMode);
+ addTest("AspectRatio", &GFXtests::aspectRatio);
+ addTest("IconifyingWindow", &GFXtests::iconifyWindow);
+
+ // Mouse Layer tests (Palettes and movements)
+ addTest("PalettizedCursors", &GFXtests::palettizedCursors);
+ addTest("MouseMovements", &GFXtests::mouseMovements);
+ // FIXME: Scaled cursor crash with odd dimmensions
+ addTest("ScaledCursors", &GFXtests::scaledCursors);
+
+ // Effects
+ addTest("shakingEffect", &GFXtests::shakingEffect);
+ // addTest("focusRectangle", &GFXtests::focusRectangle);
+
+ // Overlay
+ addTest("Overlays", &GFXtests::overlayGraphics);
+
+ // Specific Tests:
+ addTest("PaletteRotation", &GFXtests::paletteRotation);
+ addTest("cursorTrailsInGUI", &GFXtests::cursorTrails);
+ //addTest("Pixel Formats", &GFXtests::pixelFormats);
+}
+
+void GFXTestSuite::setCustomColor(uint r, uint g, uint b) {
+ _palette[8] = r;
+ _palette[9] = g;
+ _palette[10] = b;
+
+ // Set colorNum kColorSpecial with a special color.
+ int absIndx = kColorSpecial * 4;
+ _palette[absIndx + 1] = 173;
+ _palette[absIndx + 2] = 255;
+ _palette[absIndx + 3] = 47;
+ g_system->setPalette(_palette, 0, 256);
+}
+
+// Helper functions used by GFX tests
+
+void GFXtests::initMousePalette() {
+ byte palette[3 * 4]; // Black, white and yellow
+
+ palette[0] = palette[1] = palette[2] = 0;
+ palette[4] = palette[5] = palette[6] = 255;
+ palette[8] = palette[9] = 255;
+ palette[10] = 0;
+
+ CursorMan.replaceCursorPalette(palette, 0, 3);
+}
+
+Common::Rect GFXtests::computeSize(Common::Rect &cursorRect, int scalingFactor, int cursorTargetScale) {
+ if (cursorTargetScale == 1 || scalingFactor == 1) {
+ // Game data and cursor would be scaled equally.
+ // so dimensions would be same.
+ return Common::Rect(cursorRect.width(), cursorRect.height());
+ }
+
+ if (scalingFactor == 2) {
+ // Game data is scaled by 2, cursor is said to be scaled by 2 or 3. so it wud not be scaled any further
+ // So a w/2 x h/2 rectangle when scaled would match the cursor
+ return Common::Rect(cursorRect.width() / 2, cursorRect.height() / 2);
+ }
+
+ if (scalingFactor == 3) {
+ // Cursor traget scale is 2 or 3.
+ return Common::Rect((cursorRect.width() / cursorTargetScale), (cursorRect.height() / cursorTargetScale));
+ } else {
+ Testsuite::logPrintf("Unsupported scaler %dx\n", scalingFactor);
+ return Common::Rect();
+ }
+}
+
+void GFXtests::HSVtoRGB(int &rComp, int &gComp, int &bComp, int hue, int sat, int val) {
+ float r = rComp;
+ float g = gComp;
+ float b = bComp;
+
+ float h = hue * (360 / 256.0); // All colors are tried
+ float s = sat;
+ float v = val;
+
+ int i;
+ float f, p, q, t;
+
+ if (s == 0) {
+ r = g = b = v * 255;
+ return;
+ }
+
+ h /= 60;
+ i = (int)h;
+ f = h - i;
+ p = v * (1 - s);
+ q = v * (1 - s * f);
+ t = v * (1 - s * (1 - f));
+
+ switch (i) {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ default:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ rComp = (int)(r * 255);
+ gComp = (int)(g * 255);
+ bComp = (int)(b * 255);
+}
+
+Common::Rect GFXtests::drawCursor(bool cursorPaletteDisabled, const char *gfxModeName, int cursorTargetScale) {
+ // Buffer initialized with yellow color
+ byte buffer[500];
+ memset(buffer, 2, sizeof(buffer));
+
+ int cursorWidth = 12;
+ int cursorHeight = 12;
+
+ /* Disable HotSpot highlighting as of now
+
+ // Paint the cursor with yellow, except the hotspot
+ for (int i = 0; i < 16; i++) {
+ for (int j = 0; j < 16; j++) {
+ if (i != j && i != 15 - j) {
+ buffer[i * 16 + j] = 2;
+ }
+ }
+ }
+
+ */
+
+ // Uncommenting the next line and commenting the line after that would reproduce the crash
+ // CursorMan.replaceCursor(buffer, 11, 11, 0, 0, 255, cursorTargetScale);
+ CursorMan.replaceCursor(buffer, 12, 12, 0, 0, 255, cursorTargetScale);
+ CursorMan.showMouse(true);
+
+ if (cursorPaletteDisabled) {
+ CursorMan.disableCursorPalette(true);
+ } else {
+ initMousePalette();
+ CursorMan.disableCursorPalette(false);
+ }
+
+ g_system->updateScreen();
+ return Common::Rect(0, 0, cursorWidth, cursorHeight);
+}
+
+void rotatePalette(byte *palette, int size) {
+ // Rotate the colors starting from address palette "size" times
+
+ // take a temporary palette color
+ byte tColor[4] = {0};
+ // save first color in it.
+ memcpy(tColor, &palette[0], 4 * sizeof(byte));
+
+ // Move each color upward by 1
+ for (int i = 0; i < size - 1; i++) {
+ memcpy(&palette[i * 4], &palette[(i + 1) * 4], 4 * sizeof(byte));
+ }
+ // Assign last color to tcolor
+ memcpy(&palette[(size - 1) * 4], tColor, 4 * sizeof(byte));
+}
+
+/**
+ * Sets up mouse loop, exits when user clicks any of the mouse button
+ */
+void GFXtests::setupMouseLoop(bool disableCursorPalette, const char *gfxModeName, int cursorTargetScale) {
+ bool isFeaturePresent;
+ isFeaturePresent = g_system->hasFeature(OSystem::kFeatureCursorHasPalette);
+ Common::Rect cursorRect;
+
+ if (isFeaturePresent) {
+
+ cursorRect = GFXtests::drawCursor(disableCursorPalette, gfxModeName, cursorTargetScale);
+
+ Common::EventManager *eventMan = g_system->getEventManager();
+ Common::Event event;
+ Common::Point pt(0, 100);
+
+ bool quitLoop = false;
+ uint32 lastRedraw = 0;
+ const uint32 waitTime = 1000 / 45;
+
+ Testsuite::clearScreen();
+ Common::String info = disableCursorPalette ? "Using Game Palette" : "Using cursor palette";
+ info += " to render the cursor, Click to finish";
+
+ Common::String gfxScalarMode(gfxModeName);
+
+ if (!gfxScalarMode.equals("")) {
+ info = "The cursor size (yellow) should match the red rectangle.";
+ }
+
+ Testsuite::writeOnScreen(info, pt);
+
+ info = "GFX Mode";
+ info += gfxModeName;
+ info += " ";
+
+ char cScale = cursorTargetScale + '0';
+ info += "Cursor scale: ";
+ info += cScale;
+
+ Common::Rect estimatedCursorRect;
+
+ if (!gfxScalarMode.equals("")) {
+
+ if (gfxScalarMode.contains("1x")) {
+ estimatedCursorRect = computeSize(cursorRect, 1, cursorTargetScale);
+ } else if (gfxScalarMode.contains("2x")) {
+ estimatedCursorRect = computeSize(cursorRect, 2, cursorTargetScale);
+ } else if (gfxScalarMode.contains("3x")) {
+ estimatedCursorRect = computeSize(cursorRect, 3, cursorTargetScale);
+ } else {
+ // If unable to detect scaler, default to 2
+ Testsuite::writeOnScreen("Unable to detect scaling factor, assuming 2x", Common::Point(0, 5));
+ estimatedCursorRect = computeSize(cursorRect, 2, cursorTargetScale);
+ }
+ Testsuite::writeOnScreen(info, Common::Point(0, 120));
+
+ // Move cursor to (20, 20)
+ g_system->warpMouse(20, 20);
+ // Move estimated rect to (20, 20)
+ estimatedCursorRect.moveTo(20, 20);
+
+ Graphics::Surface *screen = g_system->lockScreen();
+ GFXTestSuite::setCustomColor(255, 0, 0);
+ screen->fillRect(estimatedCursorRect, 2);
+ g_system->unlockScreen();
+ g_system->updateScreen();
+ }
+
+ while (!quitLoop) {
+ while (eventMan->pollEvent(event)) {
+ if (Engine::shouldQuit()) {
+ // Quit directly
+ return;
+ }
+ if (lastRedraw + waitTime < g_system->getMillis()) {
+ g_system->updateScreen();
+ lastRedraw = g_system->getMillis();
+ }
+
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ case Common::EVENT_RBUTTONDOWN:
+ quitLoop = true;
+ Testsuite::clearScreen();
+ Testsuite::writeOnScreen("Mouse clicked", pt);
+ g_system->delayMillis(1000);
+ break;
+ default:
+ break;// Ignore handling any other event
+
+ }
+ }
+ }
+ } else {
+ Testsuite::displayMessage("feature not supported");
+ }
+}
+
+/**
+ * Used by aspectRatio()
+ */
+void GFXtests::drawEllipse(int cx, int cy, int a, int b) {
+
+ // Take a buffer of screen size
+ int width = g_system->getWidth();
+ int height = Testsuite::getDisplayRegionCoordinates().y;
+ byte *buffer = new byte[height * width];
+ double theta;
+ int x, y, x1, y1;
+
+ memset(buffer, 0, sizeof(byte) * width * height);
+ // Illuminate the center
+ buffer[cx * width + cy] = 1;
+
+ // Illuminate the points lying on ellipse
+
+ for (theta = 0; theta <= PI / 2; theta += PI / 360) {
+ x = (int)(b * sin(theta) + 0.5);
+ y = (int)(a * cos(theta) + 0.5);
+
+ // This gives us four points
+
+ x1 = x + cx;
+ y1 = y + cy;
+
+ buffer[x1 * width + y1] = 1;
+
+ x1 = (-1) * x + cx;
+ y1 = y + cy;
+
+ buffer[x1 * width + y1] = 1;
+
+ x1 = x + cx;
+ y1 = (-1) * y + cy;
+
+ buffer[x1 * width + y1] = 1;
+
+ x1 = (-1) * x + cx;
+ y1 = (-1) * y + cy;
+
+ buffer[x1 * width + y1] = 1;
+ }
+
+ g_system->copyRectToScreen(buffer, width, 0, 0, width, height);
+ g_system->updateScreen();
+ delete[] buffer;
+}
+
+// GFXtests go here
+
+/**
+ * Tests the fullscreen mode by: toggling between fullscreen and windowed mode
+ */
+TestExitStatus GFXtests::fullScreenMode() {
+ Testsuite::clearScreen();
+ Common::String info = "Fullscreen test. Here you should expect a toggle between windowed and fullscreen states depending "
+ "upon your initial state.";
+
+ Common::Point pt(0, 100);
+ Testsuite::writeOnScreen("Testing fullscreen mode", pt);
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : FullScreenMode\n");
+ return kTestSkipped;
+ }
+
+ bool isFeaturePresent;
+ bool isFeatureEnabled;
+ TestExitStatus passed = kTestPassed;
+ Common::String prompt;
+ OptionSelected shouldSelect;
+
+ isFeaturePresent = g_system->hasFeature(OSystem::kFeatureFullscreenMode);
+
+ if (isFeaturePresent) {
+ // Toggle
+ isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureFullscreenMode);
+ shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight;
+
+ g_system->delayMillis(1000);
+
+ if (isFeatureEnabled) {
+ Testsuite::logDetailedPrintf("Current Mode is Fullsecreen\n");
+ } else {
+ Testsuite::logDetailedPrintf("Current Mode is Windowed\n");
+ }
+
+ prompt = " Which mode do you see currently ? ";
+
+ if (!Testsuite::handleInteractiveInput(prompt, "Fullscreen", "Windowed", shouldSelect)) {
+ // User selected incorrect current state
+ passed = kTestFailed;
+ Testsuite::logDetailedPrintf("g_system->getFeatureState() failed\n");
+ }
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled);
+ g_system->endGFXTransaction();
+
+ // Current state should be now !isFeatureEnabled
+ isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureFullscreenMode);
+ shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight;
+
+ g_system->delayMillis(1000);
+
+ prompt = " Which screen mode do you see now ? ";
+
+ if (!Testsuite::handleInteractiveInput(prompt, "Fullscreen", "Windowed", shouldSelect)) {
+ // User selected incorrect mode
+ passed = kTestFailed;
+ Testsuite::logDetailedPrintf("g_system->setFeatureState() failed\n");
+ }
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled);
+ g_system->endGFXTransaction();
+
+ g_system->delayMillis(1000);
+
+ prompt = "This should be your initial state. Is it?";
+
+ if (!Testsuite::handleInteractiveInput(prompt, "Yes, it is", "Nopes", shouldSelect)) {
+ // User selected incorrect mode
+ Testsuite::logDetailedPrintf("switching back to initial state failed\n");
+ passed = kTestFailed;
+ }
+
+ } else {
+ Testsuite::displayMessage("feature not supported");
+ }
+
+ return passed;
+}
+
+/**
+ * Tests the aspect ratio correction by: drawing an ellipse, when corrected the ellipse should render to a circle
+ */
+TestExitStatus GFXtests::aspectRatio() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Aspect Ratio Correction test. If aspect ratio correction is enabled you should expect a circle on screen,"
+ " an ellipse otherwise.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Aspect Ratio\n");
+ return kTestSkipped;
+ }
+ // Draw an ellipse on the screen
+ drawEllipse(80, 160, 72, 60);
+
+ bool isFeaturePresent;
+ bool isFeatureEnabled;
+ TestExitStatus passed = kTestPassed;
+ Common::String prompt;
+ OptionSelected shouldSelect;
+
+ isFeaturePresent = g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection);
+ isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection);
+ g_system->delayMillis(1000);
+
+ if (isFeaturePresent) {
+ // Toggle
+ shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight;
+ prompt = " What does the curve on screen appears to you ?";
+ if (!Testsuite::handleInteractiveInput(prompt, "Circle", "Ellipse", shouldSelect)) {
+ // User selected incorrect option
+ passed = kTestFailed;
+ Testsuite::logDetailedPrintf("Aspect Ratio Correction failed\n");
+ }
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, !isFeatureEnabled);
+ g_system->endGFXTransaction();
+
+ g_system->delayMillis(1000);
+
+ shouldSelect = !isFeatureEnabled ? kOptionLeft : kOptionRight;
+ prompt = " What does the curve on screen appears to you ?";
+ if (!Testsuite::handleInteractiveInput(prompt, "Circle", "Ellipse", shouldSelect)) {
+ // User selected incorrect option
+ passed = kTestFailed;
+ Testsuite::logDetailedPrintf("Aspect Ratio Correction failed\n");
+ }
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, isFeatureEnabled);
+ g_system->endGFXTransaction();
+ } else {
+ Testsuite::displayMessage("feature not supported");
+ }
+
+ g_system->delayMillis(500);
+
+ if (Testsuite::handleInteractiveInput("This should definetely be your initial state?", "Yes, it is", "Nopes", kOptionRight)) {
+ // User selected incorrect mode
+ Testsuite::logDetailedPrintf("Switching back to initial state failed\n");
+ passed = kTestFailed;
+ }
+
+ return passed;
+}
+
+/**
+ * Tests Palettized cursors.
+ * Method: Create a yellow colored cursor, should be able to move it. Once you click test terminates
+ */
+TestExitStatus GFXtests::palettizedCursors() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Palettized Cursors test.\n "
+ "Here you should expect to see a yellow mouse cursor rendered with mouse graphics.\n"
+ "You would be able to move the cursor. Later we use game graphics to render the cursor.\n"
+ "For cursor palette it should be yellow and will be red if rendered by the game palette.\n"
+ "The test finishes when mouse (L/R) is clicked.";
+
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Palettized Cursors\n");
+ return kTestSkipped;
+ }
+
+ TestExitStatus passed = kTestPassed;
+
+ // Testing with cursor Palette
+ setupMouseLoop();
+
+ if (Testsuite::handleInteractiveInput("Which color did the cursor appeared to you?", "Yellow", "Any other", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Couldn't use cursor palette for rendering cursor\n");
+ passed = kTestFailed;
+ }
+
+ // Testing with game Palette
+ GFXTestSuite::setCustomColor(255, 0, 0);
+ setupMouseLoop(true);
+
+ if (Testsuite::handleInteractiveInput("Which color did the cursor appeared to you?", "Red", "Any other", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Couldn't use Game palette for rendering cursor\n");
+ passed = kTestFailed;
+ }
+
+ if (!Testsuite::handleInteractiveInput(" Did test run as was described? ")) {
+ passed = kTestFailed;
+ }
+
+ // re-enable cursor palette
+ CursorMan.disableCursorPalette(false);
+ // Done with cursors, make them invisible, any other test the could simply make it visible
+ CursorMan.showMouse(false);
+ return passed;
+}
+
+/**
+ * Tests automated mouse movements. "Warp" functionality provided by the backend.
+ */
+
+TestExitStatus GFXtests::mouseMovements() {
+ Testsuite::clearScreen();
+ // Ensure that the cursor is visible
+ CursorMan.showMouse(true);
+
+ Common::String info = "Testing Automated Mouse movements.\n"
+ "You should expect cursor hotspot(top-left corner) to automatically move from (0, 0) to (100, 100).\n"
+ "There we have a rectangle drawn, finally the cursor would lie centred in that rectangle.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Mouse Movements\n");
+ return kTestSkipped;
+ }
+
+ // Draw Rectangle
+ Graphics::Surface *screen = g_system->lockScreen();
+ // Ensure that 2 represents red in current palette
+ GFXTestSuite::setCustomColor(255, 0, 0);
+ screen->fillRect(Common::Rect::center(106, 106, 14, 14), 2);
+ g_system->unlockScreen();
+
+ // Testing Mouse Movements now!
+ Common::Point pt(0, 10);
+ Testsuite::writeOnScreen("Moving mouse hotspot automatically from (0, 0) to (100, 100)", pt);
+ g_system->warpMouse(0, 0);
+ g_system->updateScreen();
+ g_system->delayMillis(1000);
+
+ for (int i = 0; i <= 100; i++) {
+ g_system->delayMillis(20);
+ g_system->warpMouse(i, i);
+ g_system->updateScreen();
+ }
+
+ Testsuite::writeOnScreen("Mouse hotspot Moved to (100, 100)", pt);
+ g_system->delayMillis(1500);
+ CursorMan.showMouse(false);
+
+ if (Testsuite::handleInteractiveInput("Was the cursor centred in the rectangle at (100, 100)?", "Yes", "No", kOptionRight)) {
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+
+
+/**
+ * This basically blits the screen by the contents of its buffer.
+ *
+ */
+TestExitStatus GFXtests::copyRectToScreen() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing Blitting a Bitmap to screen.\n"
+ "You should expect to see a 20x40 yellow horizontal rectangle centred at the screen.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Blitting Bitmap\n");
+ return kTestSkipped;
+ }
+
+ GFXTestSuite::setCustomColor(255, 255, 0);
+ byte buffer[20 * 40];
+ memset(buffer, 2, 20 * 40);
+
+ uint x = g_system->getWidth() / 2 - 20;
+ uint y = g_system->getHeight() / 2 - 10;
+
+ g_system->copyRectToScreen(buffer, 40, x, y, 40, 20);
+ g_system->updateScreen();
+ g_system->delayMillis(1000);
+
+ if (Testsuite::handleInteractiveInput(" Did you see yellow rectangle ? ", "Yes", "No", kOptionRight)) {
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+/**
+ * Testing feature : Iconifying window
+ * It is expected the screen minimizes when this feature is enabled
+ */
+TestExitStatus GFXtests::iconifyWindow() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing Iconify Window mode.\n If the feature is supported by the backend, "
+ "you should expect the window to be minimized.\n However you would manually need to de-iconify.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Iconifying window\n");
+ return kTestSkipped;
+ }
+
+ Common::Point pt(0, 100);
+ Testsuite::writeOnScreen("Testing Iconifying window", pt);
+
+ bool isFeaturePresent;
+ bool isFeatureEnabled;
+
+ isFeaturePresent = g_system->hasFeature(OSystem::kFeatureIconifyWindow);
+ isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureIconifyWindow);
+ g_system->delayMillis(1000);
+
+ if (isFeaturePresent) {
+ // Toggle
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureIconifyWindow, !isFeatureEnabled);
+ g_system->endGFXTransaction();
+
+ g_system->delayMillis(1000);
+
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureIconifyWindow, isFeatureEnabled);
+ g_system->endGFXTransaction();
+ } else {
+ Testsuite::displayMessage("feature not supported");
+ }
+
+ if (Testsuite::handleInteractiveInput(" Did you see the window minimized? ", "Yes", "No", kOptionRight)) {
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+/**
+ * Testing feature: Scaled cursors
+ */
+TestExitStatus GFXtests::scaledCursors() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing : Scaled cursors\n"
+ "Here every graphics mode is tried with a cursorTargetScale of 1, 2 and 3.\n"
+ "The expected cursor size is drawn as a rectangle.\n The cursor should approximately match that rectangle.\n"
+ "This may take time, You may skip the later scalers and just examine the first three i.e 1x, 2x and 3x";
+
+ bool isAspectRatioCorrected = g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection);
+
+ if (isAspectRatioCorrected) {
+ info += "\nDisabling Aspect ratio correction, for letting cusors match exactly, will be restored after this test.";
+ }
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Scaled Cursors\n");
+ return kTestSkipped;
+ }
+
+ int maxLimit = 1000;
+ if (!Testsuite::handleInteractiveInput("Do You want to restrict scalers to 1x, 2x and 3x only?", "Yes", "No", kOptionRight)) {
+ maxLimit = 3;
+ }
+
+
+ if (isAspectRatioCorrected) {
+ g_system->beginGFXTransaction();
+ g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, false);
+ g_system->endGFXTransaction();
+ }
+
+ const int currGFXMode = g_system->getGraphicsMode();
+ const OSystem::GraphicsMode *gfxMode = g_system->getSupportedGraphicsModes();
+
+ while (gfxMode->name && maxLimit > 0) {
+ // for every graphics mode display cursors for cursorTargetScale 1, 2 and 3
+ // Switch Graphics mode
+ // FIXME: Crashes with "3x" mode now.:
+
+ info = Common::String::printf("Testing : Scaled cursors with GFX Mode %s\n", gfxMode->name);
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("\tInfo! Skipping sub-test : Scaled Cursors :: GFX Mode %s\n", gfxMode->name);
+ gfxMode++;
+ maxLimit--;
+ continue;
+ }
+ if (Engine::shouldQuit()) {
+ // Explicit exit requested
+ Testsuite::logPrintf("Info! Explicit exit requested during scaling test, this test may be incomplete\n");
+ return kTestSkipped;
+ }
+
+ g_system->beginGFXTransaction();
+
+ bool isGFXModeSet = g_system->setGraphicsMode(gfxMode->id);
+ g_system->initSize(320, 200);
+
+ OSystem::TransactionError gfxError = g_system->endGFXTransaction();
+
+ if (gfxError == OSystem::kTransactionSuccess && isGFXModeSet) {
+ setupMouseLoop(false, gfxMode->name, 1);
+ Testsuite::clearScreen();
+
+ setupMouseLoop(false, gfxMode->name, 2);
+ Testsuite::clearScreen();
+
+ setupMouseLoop(false, gfxMode->name, 3);
+ Testsuite::clearScreen();
+ } else {
+ Testsuite::logDetailedPrintf("Switching to graphics mode %s failed\n", gfxMode->name);
+ return kTestFailed;
+ }
+ gfxMode++;
+ maxLimit--;
+
+ info = "Did the expected cursor size and the actual cursor size matched?";
+ if (Testsuite::handleInteractiveInput(info, "Yes", "No", kOptionRight)) {
+ Testsuite::logPrintf("\tInfo! Failed sub-test : Scaled Cursors :: GFX Mode %s\n", gfxMode->name);
+ }
+
+ if (Engine::shouldQuit()) {
+ // Explicit exit requested
+ Testsuite::logPrintf("Info! Explicit exit requested during scaling test, this test may be incomplete\n");
+ return kTestSkipped;
+ }
+
+ }
+
+ // Restore Original State
+ g_system->beginGFXTransaction();
+ bool isGFXModeSet = g_system->setGraphicsMode(currGFXMode);
+ g_system->initSize(320, 200);
+
+ if (isAspectRatioCorrected) {
+ g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, true);
+ }
+
+ OSystem::TransactionError gfxError = g_system->endGFXTransaction();
+
+ if (gfxError != OSystem::kTransactionSuccess || !isGFXModeSet) {
+ Testsuite::logDetailedPrintf("Switcing to initial state failed\n");
+ return kTestFailed;
+ }
+
+ // Done with cursors, Make them invisible, any other test may enable and use it.
+ CursorMan.showMouse(false);
+ return kTestPassed;
+}
+
+TestExitStatus GFXtests::shakingEffect() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Shaking test. You should expect the graphics(text/bars etc) drawn on the screen to shake!";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Shaking Effect\n");
+ return kTestSkipped;
+ }
+
+ Common::Point pt(0, 100);
+ Testsuite::writeOnScreen("If Shaking Effect works, this should shake!", pt);
+ int times = 15;
+ while (times--) {
+ g_system->setShakePos(25);
+ g_system->delayMillis(50);
+ g_system->updateScreen();
+ g_system->setShakePos(0);
+ g_system->delayMillis(50);
+ g_system->updateScreen();
+ }
+ g_system->delayMillis(1500);
+
+ if (Testsuite::handleInteractiveInput("Did the Shaking test worked as you were expecting?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Shaking Effect didn't worked");
+ return kTestFailed;
+ }
+ return kTestPassed;
+}
+
+TestExitStatus GFXtests::focusRectangle() {
+
+ Testsuite::clearScreen();
+ Common::String info = "Testing : Setting and hiding Focus \n"
+ "If this feature is implemented, the focus should be toggled between the two rectangles on the corners";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : focus Rectangle\n");
+ return kTestSkipped;
+ }
+
+ const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont));
+
+ Graphics::Surface *screen = g_system->lockScreen();
+ int screenHeight = g_system->getHeight();
+ int screenWidth = g_system->getWidth();
+
+ int height = font.getFontHeight();
+ int width = screenWidth / 2;
+
+ Common::Rect rectLeft(0, 0, width, height * 2);
+ screen->fillRect(rectLeft, kColorWhite);
+ font.drawString(screen, "Focus 1", rectLeft.left, rectLeft.top, width, kColorBlack, Graphics::kTextAlignLeft);
+
+ Common::Rect rectRight(screenWidth - width, screenHeight - height * 2 , screenWidth, screenHeight);
+ screen->fillRect(rectRight, kColorWhite);
+ font.drawString(screen, "Focus 2", rectRight.left, rectRight.top, width, kColorBlack, Graphics::kTextAlignRight);
+ g_system->unlockScreen();
+ g_system->updateScreen();
+
+ g_system->clearFocusRectangle();
+
+ g_system->setFocusRectangle(rectLeft);
+ g_system->updateScreen();
+
+ g_system->delayMillis(1000);
+
+ g_system->setFocusRectangle(rectRight);
+ g_system->updateScreen();
+
+ g_system->clearFocusRectangle();
+
+ if (Testsuite::handleInteractiveInput("Did you noticed a variation in focus?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Focus Rectangle feature doesn't works. Check platform.\n");
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus GFXtests::overlayGraphics() {
+ Testsuite::clearScreen();
+ Common::String info = "Overlay Graphics. You should expect to see a green colored rectangle on the screen";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Overlay Graphics\n");
+ return kTestSkipped;
+ }
+
+ Graphics::PixelFormat pf = g_system->getOverlayFormat();
+
+ OverlayColor buffer[50 * 100];
+ OverlayColor value = pf.RGBToColor(0, 255, 0);
+
+ for (int i = 0; i < 50 * 100; i++) {
+ buffer[i] = value;
+ }
+
+ g_system->showOverlay();
+ g_system->copyRectToOverlay(buffer, 100, 270, 175, 100, 50);
+ g_system->updateScreen();
+
+ g_system->delayMillis(1000);
+
+ g_system->hideOverlay();
+ g_system->updateScreen();
+
+ if (Testsuite::handleInteractiveInput("Did you see a green overlayed rectangle?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Overlay Rectangle feature doesn't works\n");
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus GFXtests::paletteRotation() {
+
+ Common::String info = "Palette rotation. Here we draw a full 256 colored rainbow and then rotate it.\n"
+ "Note that the screen graphics change without having to draw anything.\n"
+ "The palette should appear to rotate, as a result, the background will change its color too.\n"
+ "Click the mouse button to exit.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : palette Rotation\n");
+ return kTestSkipped;
+ }
+ Common::Point pt(0, 10);
+ Testsuite::clearEntireScreen();
+
+ // Use 256 colors
+ byte palette[256 * 4] = {0};
+
+ int r, g, b;
+ int colIndx;
+
+ for (int i = 0; i < 256; i++) {
+ HSVtoRGB(r, g, b, i, 1, 1);
+ colIndx = i * 4;
+ palette[colIndx] = r;
+ palette[colIndx + 1] = g;
+ palette[colIndx + 2] = b;
+ }
+
+ // Initialize this palette.
+ g_system->setPalette(palette, 0, 256);
+
+ // Draw 256 Rectangles, each 1 pixel wide and 10 pixels long
+ // one for 0-255 color range other for 0-127-255 range
+ byte buffer[256 * 30] = {0};
+
+ for (int i = 0; i < 30; i++) {
+ for (int j = 0; j < 256; j++) {
+ if (i < 10) {
+ buffer[i * 256 + j] = j + 2;
+ } else if (i < 20) {
+ buffer[i * 256 + j] = 0;
+ } else {
+ buffer[i * 256 + j] = ((j + 127) % 256) + 2;
+ }
+ }
+ }
+
+ g_system->copyRectToScreen(buffer, 256, 22, 50, 256, 30);
+
+ // Show mouse
+ CursorMan.showMouse(true);
+ g_system->updateScreen();
+
+
+ bool toRotate = true;
+ Common::Event event;
+
+ while (toRotate) {
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN) {
+ toRotate = false;
+ }
+ }
+
+ rotatePalette(palette, 256);
+
+ g_system->delayMillis(10);
+ g_system->setPalette(palette, 0, 256);
+ g_system->updateScreen();
+ }
+
+ CursorMan.showMouse(false);
+ // Reset initial palettes
+ GFXTestSuite::setCustomColor(255, 0, 0);
+ Testsuite::clearScreen();
+
+ if(Testsuite::handleInteractiveInput("Did you see a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) {
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus GFXtests::cursorTrails() {
+ Common::String info = "With some shake offset the cursor was known to leave trails in the GUI\n"
+ "Here we set some offset and ask user to check for mouse trails, \n"
+ "the test is passed when there are no trails";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Cursor Trails\n");
+ return kTestSkipped;
+ }
+ TestExitStatus passed = kTestFailed;
+ g_system->setShakePos(25);
+ g_system->updateScreen();
+ if (Testsuite::handleInteractiveInput("Does the cursor leaves trails while moving?", "Yes", "No", kOptionRight)) {
+ passed = kTestPassed;
+ }
+ g_system->setShakePos(0);
+ g_system->updateScreen();
+ return passed;
+}
+
+TestExitStatus GFXtests::pixelFormats() {
+ Testsuite::clearScreen();
+ Common::String info = "Testing pixel formats. Here we iterate over all the supported pixel formats and display some colors using them\n"
+ "This may take long, especially if the backend supports many pixel formats";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Pixel Formats\n");
+ return kTestSkipped;
+ }
+
+ Common::List<Graphics::PixelFormat> pfList = g_system->getSupportedFormats();
+ Common::List<Graphics::PixelFormat>::const_iterator iter = pfList.begin();
+
+ int numFormatsTested = 0;
+ int numPassed = 0;
+ int numFailed = 0;
+
+ Testsuite::logDetailedPrintf("Testing Pixel Formats. Size of list : %d\n", pfList.size());
+
+ for (iter = pfList.begin(); iter != pfList.end(); iter++) {
+ numFormatsTested++;
+ if (iter->bytesPerPixel == 1) {
+ // Palettes already tested
+ continue;
+ } else if (iter->bytesPerPixel > 2) {
+ Testsuite::logDetailedPrintf("Can't test pixels with bpp > 2\n");
+ continue;
+ }
+
+ // Switch to that pixel Format
+ g_system->beginGFXTransaction();
+ g_system->initSize(320, 200, &(*iter));
+ g_system->endGFXTransaction();
+ Testsuite::clearScreen(true);
+
+ // Draw some nice gradients
+ // Pick up some colors
+ uint colors[6];
+
+ colors[0] = iter->RGBToColor(255, 255, 255);
+ colors[1] = iter->RGBToColor(135, 48, 21);
+ colors[2] = iter->RGBToColor(205, 190, 87);
+ colors[3] = iter->RGBToColor(0, 32, 64);
+ colors[4] = iter->RGBToColor(181, 126, 145);
+ colors[5] = iter->RGBToColor(47, 78, 36);
+
+ Common::Point pt(0, 170);
+ Common::String msg;
+ msg = Common::String::printf("Testing Pixel Formats, %d of %d", numFormatsTested, pfList.size());
+ Testsuite::writeOnScreen(msg, pt, true);
+
+ // CopyRectToScreen could have been used, but that may involve writing code which
+ // already resides in graphics/surface.h
+ // So using Graphics::Surface
+
+ Graphics::Surface *screen = g_system->lockScreen();
+
+ // Draw 6 rectangles centred at (50, 160), piled over one another
+ // each with color in colors[]
+ for (int i = 0; i < 6; i++) {
+ screen->fillRect(Common::Rect::center(160, 20 + i * 10, 100, 10), colors[i]);
+ }
+
+ g_system->unlockScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(500);
+
+ if(Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) {
+ numPassed++;
+ } else {
+ numFailed++;
+ Testsuite::logDetailedPrintf("Testing pixel format failed for format #%d on the list\n", numFormatsTested);
+ }
+ }
+
+ // Revert back to 8bpp
+ g_system->beginGFXTransaction();
+ g_system->initSize(320, 200);
+ g_system->endGFXTransaction();
+ GFXTestSuite::setCustomColor(255, 0, 0);
+ initMousePalette();
+ Testsuite::clearScreen();
+
+ if (numFailed) {
+ Testsuite::logDetailedPrintf("Pixel Format test: Failed : %d, Passed : %d, Ignored %d\n",numFailed, numPassed, numFormatsTested - (numPassed + numFailed));
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/graphics.h b/engines/testbed/graphics.h
new file mode 100644
index 0000000000..4ab4ba65ab
--- /dev/null
+++ b/engines/testbed/graphics.h
@@ -0,0 +1,94 @@
+/* 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 TESTBED_GRAPHICS_H
+#define TESTBED_GRAPHICS_H
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+namespace GFXtests {
+
+// Helper functions for GFX tests
+void drawEllipse(int x, int y, int a, int b);
+void setupMouseLoop(bool disableCursorPalette = false, const char *gfxModeName = "", int cursorTargetScale = 1);
+void initMousePalette();
+Common::Rect computeSize(Common::Rect &cursorRect, int scalingFactor, int cursorTargetScale);
+void HSVtoRGB(int &rComp, int &gComp, int &bComp, int hue, int sat, int val);
+Common::Rect drawCursor(bool cursorPaletteDisabled = false, const char *gfxModeName = "", int cursorTargetScale = 1);
+
+// will contain function declarations for GFX tests
+TestExitStatus cursorTrails();
+TestExitStatus fullScreenMode();
+TestExitStatus aspectRatio();
+TestExitStatus palettizedCursors();
+TestExitStatus mouseMovements();
+TestExitStatus copyRectToScreen();
+TestExitStatus iconifyWindow();
+TestExitStatus scaledCursors();
+TestExitStatus shakingEffect();
+TestExitStatus focusRectangle();
+TestExitStatus overlayGraphics();
+TestExitStatus paletteRotation();
+TestExitStatus pixelFormats();
+// add more here
+
+} // End of namespace GFXtests
+
+class GFXTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the GFXTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ GFXTestSuite();
+ ~GFXTestSuite() {}
+ const char *getName() const {
+ return "GFX";
+ }
+ const char *getDescription() const {
+ return "Graphics Subsystem";
+ }
+ static void setCustomColor(uint r, uint g, uint b);
+
+private:
+ /**
+ * A Palette consists of 4 components RGBA.
+ * As of now we only take 3 colors
+ * 0 (R:0, G:0, B:0) Black (kColorBlack)
+ * 1 (R:255, G:255, B:255) White (kColorWhite)
+ * 2 (R:255, G:255, B:255) your customized color (by default white) (kColorCustom)
+ * The remaining values are zero
+ */
+ static byte _palette[256 * 4];
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_GRAPHICS_H
diff --git a/engines/testbed/midi.cpp b/engines/testbed/midi.cpp
new file mode 100644
index 0000000000..0ec2678d47
--- /dev/null
+++ b/engines/testbed/midi.cpp
@@ -0,0 +1,155 @@
+/* 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/archive.h"
+#include "common/events.h"
+
+#include "graphics/cursorman.h"
+
+#include "sound/mididrv.h"
+
+#include "testbed/midi.h"
+#include "testbed/testbed.h"
+
+namespace Testbed {
+
+bool MidiTests::loadMusicInMemory(Common::MemoryWriteStreamDynamic *ws) {
+ Common::SeekableReadStream *midiFile = SearchMan.createReadStreamForMember("music.mid");
+ if (!midiFile) {
+ Testsuite::logPrintf("Error! Can't open Midi music file, check game data directory for file music.mid\n");
+ return false;
+ }
+
+ while (!midiFile->eos()) {
+ byte data = midiFile->readByte();
+ ws->writeByte(data);
+ }
+ return true;
+}
+
+void MidiTests::waitForMusicToPlay(MidiParser *parser) {
+ Common::EventManager *eventMan = g_system->getEventManager();
+ bool quitLoop = false;
+ Common::Event event;
+
+ CursorMan.showMouse(true);
+ while (!quitLoop) {
+ while (eventMan->pollEvent(event)) {
+ // Quit if explicitly requested!
+ if (Engine::shouldQuit()) {
+ return;
+ }
+
+ if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN) {
+ quitLoop = true;
+ } else {
+ Testsuite::writeOnScreen("Playing Midi Music, Click to end", Common::Point(0, 100));
+ if (!parser->isPlaying()) {
+ quitLoop = true;
+ }
+ }
+ }
+ }
+ CursorMan.showMouse(false);
+ return;
+}
+
+TestExitStatus MidiTests::playMidiMusic() {
+ Testsuite::clearScreen();
+ Common::String info = "Testing Midi Sound output.\n"
+ "Here, We generate some Music by using the Midi Driver selected in the GUI.\n"
+ "You should expect to hear that. The initialization may take some time.\n";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Play Midi Music\n");
+ return kTestSkipped;
+ }
+
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
+ // Create a driver instance
+ MidiDriver *driver = MidiDriver::createMidi(dev);
+ // Create a SMF parser
+ MidiParser *smfParser = MidiParser::createParser_SMF();
+ // Open the driver
+ int errCode = driver->open();
+
+ if (errCode) {
+ Common::String errMsg = driver->getErrorName(errCode);
+ Testsuite::writeOnScreen(errMsg, Common::Point(0, 100));
+ Testsuite::logPrintf("Error! %s", errMsg.c_str());
+ return kTestFailed;
+ }
+
+ Testsuite::logDetailedPrintf("Info! Midi: Succesfully opened the driver\n");
+
+ Common::MemoryWriteStreamDynamic ws(DisposeAfterUse::YES);
+ loadMusicInMemory(&ws);
+
+ // start playing
+ if (smfParser->loadMusic(ws.getData(), ws.size())) {
+ smfParser->setTrack(0);
+ smfParser->setMidiDriver(driver);
+ smfParser->setTimerRate(driver->getBaseTempo());
+ driver->setTimerCallback(smfParser, MidiParser::timerCallback);
+ Testsuite::logDetailedPrintf("Info! Midi: Parser Successfully loaded Music data.\n");
+ if (smfParser->isPlaying()) {
+ Testsuite::writeOnScreen("Playing Midi Music, Click to end.", Common::Point(0, 100));
+ Testsuite::logDetailedPrintf("Info! Midi: Playing music!\n");
+ }
+ }
+
+
+ // Play until track ends or an exit is requested.
+ waitForMusicToPlay(smfParser);
+
+ // Done. Clean up.
+ smfParser->unloadMusic();
+ driver->setTimerCallback(NULL, NULL);
+ driver->close();
+ delete smfParser;
+ delete driver;
+
+ if (Testsuite::handleInteractiveInput("Were you able to hear the music as described?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! Midi: Can't play Music\n");
+ return kTestFailed;
+ }
+ return kTestPassed;
+}
+
+MidiTestSuite::MidiTestSuite() {
+ addTest("MidiTests", &MidiTests::playMidiMusic);
+ _isMidiDataFound = true;
+ if (!SearchMan.hasFile("music.mid")) {
+ // add some fallback test if filesystem loading failed
+ Testsuite::logPrintf("Warning! Midi: Sound data file music.mid not found\n");
+ _isMidiDataFound = false;
+ enable(false);
+ }
+}
+
+void MidiTestSuite::enable(bool flag) {
+ Testsuite::enable(_isMidiDataFound & flag);
+}
+
+}
diff --git a/engines/testbed/midi.h b/engines/testbed/midi.h
new file mode 100644
index 0000000000..c73d937834
--- /dev/null
+++ b/engines/testbed/midi.h
@@ -0,0 +1,77 @@
+/* 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 TESTBED_MIDI_H
+#define TESTBED_MIDI_H
+
+#include "common/stream.h"
+#include "sound/midiparser.h"
+#include "testbed/testsuite.h"
+
+// This file can be used as template for header files of other newer testsuites.
+
+namespace Testbed {
+
+namespace MidiTests {
+
+// Helper functions for MIDI tests
+bool loadMusicInMemory(Common::MemoryWriteStreamDynamic *ws);
+void waitForMusicToPlay(MidiParser *parser);
+
+// will contain function declarations for MIDI tests
+// add more here
+TestExitStatus playMidiMusic();
+
+} // End of namespace MIDItests
+
+class MidiTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the XXXTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ MidiTestSuite();
+ ~MidiTestSuite() {}
+ const char *getName() const {
+ return "MIDI";
+ }
+
+ const char *getDescription() const {
+ return "Midi Music";
+ }
+
+ void enable(bool flag);
+
+private:
+ bool _isMidiDataFound;
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_MIDI_H
diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp
new file mode 100644
index 0000000000..2159974c51
--- /dev/null
+++ b/engines/testbed/misc.cpp
@@ -0,0 +1,172 @@
+/* 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 "testbed/misc.h"
+#include "common/timer.h"
+
+namespace Testbed {
+
+Common::String MiscTests::getHumanReadableFormat(TimeDate &td) {
+ return Common::String::printf("%d:%d:%d on %d/%d/%d (dd/mm/yyyy)", td.tm_hour, td.tm_min, td.tm_sec, td.tm_mday, td.tm_mon + 1, td.tm_year + 1900);
+}
+
+void MiscTests::timerCallback(void *arg) {
+ // Increment arg which actually points to an int
+ // arg must point to a static data, threads otherwise have their own stack
+ int &valToModify = *((int *) arg);
+ valToModify = 999; // some arbitrary value
+}
+
+void MiscTests::criticalSection(void *arg) {
+ SharedVars &sv = *((SharedVars *)arg);
+
+ Testsuite::logDetailedPrintf("Before critical section: %d %d\n", sv.first, sv.second);
+ g_system->lockMutex(sv.mutex);
+
+ // In any case, the two vars must be equal at entry, if mutex works fine.
+ // verify this here.
+ if (sv.first != sv.second) {
+ sv.resultSoFar = false;
+ }
+
+ sv.first++;
+ g_system->delayMillis(1000);
+
+ // This should bring no change as well in the difference between vars
+ // verify this too.
+ if (sv.second + 1 != sv.first) {
+ sv.resultSoFar = false;
+ }
+
+ sv.second *= sv.first;
+ Testsuite::logDetailedPrintf("After critical section: %d %d\n", sv.first, sv.second);
+ g_system->unlockMutex(sv.mutex);
+
+ g_system->getTimerManager()->removeTimerProc(criticalSection);
+}
+
+TestExitStatus MiscTests::testDateTime() {
+
+ if (ConfParams.isSessionInteractive()) {
+ if (Testsuite::handleInteractiveInput("Testing the date time API implementation", "Continue", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Date time tests skipped by the user.\n");
+ return kTestSkipped;
+ }
+
+ Testsuite::writeOnScreen("Verifying Date-Time...", Common::Point(0, 100));
+ }
+
+ TimeDate t1, t2;
+ g_system->getTimeAndDate(t1);
+ Testsuite::logDetailedPrintf("Current Time and Date: ");
+ Common::String dateTimeNow;
+ dateTimeNow = getHumanReadableFormat(t1);
+
+ if (ConfParams.isSessionInteractive()) {
+ // Directly verify date
+ dateTimeNow = "We expect the current date time to be " + dateTimeNow;
+ if (Testsuite::handleInteractiveInput(dateTimeNow, "Correct!", "Wrong", kOptionRight)) {
+ return kTestFailed;
+ }
+ }
+
+ g_system->getTimeAndDate(t1);
+ dateTimeNow = getHumanReadableFormat(t1);
+ Testsuite::logDetailedPrintf("%s\n", dateTimeNow.c_str());
+ // Now, Put some delay
+ g_system->delayMillis(2000);
+ g_system->getTimeAndDate(t2);
+ Testsuite::logDetailedPrintf("Time and Date 2s later: ");
+ dateTimeNow = getHumanReadableFormat(t2);
+ Testsuite::logDetailedPrintf("%s\n", dateTimeNow.c_str());
+
+ if (t1.tm_year == t2.tm_year && t1.tm_mon == t2.tm_mon && t1.tm_mday == t2.tm_mday) {
+ if (t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year) {
+ // Ignore lag due to processing time
+ if (t1.tm_sec + 2 == t2.tm_sec) {
+ return kTestPassed;
+ }
+ }
+ }
+ return kTestFailed;
+}
+
+TestExitStatus MiscTests::testTimers() {
+ static int valToModify = 0;
+ if (g_system->getTimerManager()->installTimerProc(timerCallback, 100000, &valToModify)) {
+ g_system->delayMillis(150);
+ g_system->getTimerManager()->removeTimerProc(timerCallback);
+
+ if (999 == valToModify) {
+ return kTestPassed;
+ }
+ }
+ return kTestFailed;
+}
+
+TestExitStatus MiscTests::testMutexes() {
+
+ if (ConfParams.isSessionInteractive()) {
+ if (Testsuite::handleInteractiveInput("Testing the Mutual Exclusion API implementation", "Continue", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Mutex tests skipped by the user.\n");
+ return kTestSkipped;
+ }
+ Testsuite::writeOnScreen("Installing mutex", Common::Point(0, 100));
+ }
+
+ static SharedVars sv = {1, 1, true, g_system->createMutex()};
+
+ if (g_system->getTimerManager()->installTimerProc(criticalSection, 100000, &sv)) {
+ g_system->delayMillis(150);
+ }
+
+ g_system->lockMutex(sv.mutex);
+ sv.first++;
+ g_system->delayMillis(1000);
+ sv.second *= sv.first;
+ g_system->unlockMutex(sv.mutex);
+
+ // wait till timed process exits
+ if (ConfParams.isSessionInteractive()) {
+ Testsuite::writeOnScreen("Waiting for 3s so that timed processes finish", Common::Point(0, 100));
+ }
+ g_system->delayMillis(3000);
+
+ Testsuite::logDetailedPrintf("Final Value: %d %d\n", sv.first, sv.second);
+ g_system->deleteMutex(sv.mutex);
+
+ if (sv.resultSoFar && 6 == sv.second) {
+ return kTestPassed;
+ }
+
+ return kTestFailed;
+}
+
+MiscTestSuite::MiscTestSuite() {
+ addTest("Datetime", &MiscTests::testDateTime, false);
+ addTest("Timers", &MiscTests::testTimers, false);
+ addTest("Mutexes", &MiscTests::testMutexes, false);
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/misc.h b/engines/testbed/misc.h
new file mode 100644
index 0000000000..395955c7fe
--- /dev/null
+++ b/engines/testbed/misc.h
@@ -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$
+ */
+
+#ifndef TESTBED_MISC_H
+#define TESTBED_MISC_H
+
+#include "testbed/testsuite.h"
+
+
+namespace Testbed {
+
+// Shared variables used in mutex handling test
+struct SharedVars {
+ int first;
+ int second;
+ bool resultSoFar;
+ OSystem::MutexRef mutex;
+};
+
+namespace MiscTests {
+
+// Miscellaneous tests include testing datetime, timers and mutexes
+
+// Helper functions for Misc tests
+Common::String getHumanReadableFormat(TimeDate &td);
+void timerCallback(void *arg);
+void criticalSection(void *arg);
+
+// will contain function declarations for Misc tests
+TestExitStatus testDateTime();
+TestExitStatus testTimers();
+TestExitStatus testMutexes();
+// add more here
+
+} // End of namespace MiscTests
+
+class MiscTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the MiscTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ MiscTestSuite();
+ ~MiscTestSuite() {}
+ const char *getName() const {
+ return "Misc";
+ }
+ const char *getDescription() const {
+ return "Miscellaneous: Timers/Mutexes/Datetime";
+ }
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_MISC_H
diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk
new file mode 100644
index 0000000000..ce78a48bc5
--- /dev/null
+++ b/engines/testbed/module.mk
@@ -0,0 +1,26 @@
+MODULE := engines/testbed
+
+MODULE_OBJS := \
+ config.o \
+ config-params.o \
+ detection.o \
+ events.o \
+ fs.o \
+ graphics.o \
+ midi.o \
+ misc.o \
+ savegame.o \
+ sound.o \
+ testbed.o \
+ testsuite.o
+
+MODULE_DIRS += \
+ engines/testbed
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TESTBED), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/testbed/savegame.cpp b/engines/testbed/savegame.cpp
new file mode 100644
index 0000000000..b91d9fc47c
--- /dev/null
+++ b/engines/testbed/savegame.cpp
@@ -0,0 +1,199 @@
+/* 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/savefile.h"
+
+#include "testbed/savegame.h"
+
+namespace Testbed {
+
+/**
+ * This test creates a savefile for the given testbed-state and could be reloaded using the saveFile API.
+ * It is intended to test saving and loading from savefiles.
+ */
+bool SaveGametests::writeDataToFile(const char *fileName, const char *msg) {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::OutSaveFile *saveFile = saveFileMan->openForSaving(fileName);
+
+ if (!saveFile) {
+ Testsuite::logDetailedPrintf("Can't open saveFile %s\n", fileName);
+ return false;
+ }
+
+ saveFile->writeString(msg);
+ saveFile->finalize();
+ delete saveFile;
+
+ return true;
+}
+
+bool SaveGametests::readAndVerifyData(const char *fileName, const char *expected) {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::InSaveFile *loadFile = saveFileMan->openForLoading(fileName);
+
+ if (!loadFile) {
+ Testsuite::logDetailedPrintf("Can't open save File to load\n");
+ return false;
+ }
+
+ Common::String lineToRead = loadFile->readLine();
+ delete loadFile;
+
+ if (lineToRead.equals(expected)) {
+ return true;
+ }
+
+ return false;
+}
+
+TestExitStatus SaveGametests::testSaveLoadState() {
+ // create a savefile with "ScummVM Rocks!" written on it
+ if (!writeDataToFile("tBedSavefile.0", "ScummVM Rocks!")) {
+ Testsuite::logDetailedPrintf("Writing data to savefile failed\n");
+ return kTestFailed;
+ }
+
+ // Verify if it contains the same data
+ if (!readAndVerifyData("tBedSavefile.0", "ScummVM Rocks!")) {
+ Testsuite::logDetailedPrintf("Reading data from savefile failed\n");
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus SaveGametests::testRemovingSavefile() {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+
+ // Create a dummy savefile
+ if (!writeDataToFile("tBedSavefileToRemove.0", "Dummy Savefile!")) {
+ Testsuite::logDetailedPrintf("Writing data to savefile failed\n");
+ return kTestFailed;
+ }
+
+ // Remove it
+ saveFileMan->removeSavefile("tBedSavefileToRemove.0");
+
+ // Try opening it Now
+ Common::InSaveFile *loadFile = saveFileMan->openForLoading("saveFile.0");
+ if (loadFile) {
+ // Removing failed
+ Testsuite::logDetailedPrintf("Removing savefile failed\n");
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus SaveGametests::testRenamingSavefile() {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ // Open a file for renaming
+ if (!writeDataToFile("tBedSomeWeirdName.0", "Rename me!")) {
+ Testsuite::logDetailedPrintf("Writing data to savefile failed\n");
+ return kTestFailed;
+ }
+
+ // Rename it
+ saveFileMan->renameSavefile("tBedSomeWeirdName.0", "tBedSomeCoolName.0");
+
+ // Verify if it contains the same data
+ if (!readAndVerifyData("tBedSomeCoolName.0", "Rename me!")) {
+ Testsuite::logDetailedPrintf("Renaming savefile failed\n");
+ return kTestFailed;
+ }
+
+ return kTestPassed;
+}
+
+TestExitStatus SaveGametests::testListingSavefile() {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ saveFileMan->clearError();
+
+ // create some savefiles
+ const char *savefileName[] = {"tBedSavefileToList.0", "tBedSavefileToList.1", "tBedSavefileToList.2"};
+ writeDataToFile("tBedSavefileToList.0", "Save me!");
+ writeDataToFile("tBedSavefileToList.1", "Save me!");
+ writeDataToFile("tBedSavefileToList.2", "Save me!");
+
+ Common::Error error = saveFileMan->getError();
+
+ if (error != Common::kNoError) {
+ // Abort. Some Error in writing files
+ Testsuite::logDetailedPrintf("Error while creating savefiles: %s\n", Common::errorToString(error));
+ return kTestFailed;
+ }
+
+ Common::StringArray savefileList = saveFileMan->listSavefiles("tBedSavefileToList.?");
+ if (savefileList.size() == ARRAYSIZE(savefileName)) {
+ // Match them exactly
+ // As the order of savefileList may be platform specific, match them exhaustively
+ for (uint i = 0; i < ARRAYSIZE(savefileName); i++) {
+ for (uint j = 0; j < savefileList.size(); j++) {
+ if (savefileList[j].equals(savefileName[i])) {
+ break;
+ }
+ if (savefileList.size() == j) {
+ // A match for this name not found
+ Testsuite::logDetailedPrintf("Listed Names don't match\n");
+ return kTestFailed;
+ }
+ }
+ }
+ return kTestPassed;
+ } else {
+ Testsuite::logDetailedPrintf("listing Savefiles failed!\n");
+ return kTestFailed;
+ }
+
+ return kTestFailed;
+}
+
+TestExitStatus SaveGametests::testErrorMessages() {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ saveFileMan->clearError();
+
+ // Try opening a non existing file
+ readAndVerifyData("tBedSomeNonExistentSaveFile.0", "File doesn't exists!");
+
+ Common::Error error = saveFileMan->getError();
+ if (error == Common::kNoError) {
+ // blunder! how come?
+ Testsuite::logDetailedPrintf("SaveFileMan.getError() failed\n");
+ return kTestFailed;
+ }
+ // Can't actually predict whether which error, kInvalidPath or kPathDoesNotExist or some other?
+ // So just return kTestPassed if some error
+ Testsuite::logDetailedPrintf("getError returned : %s\n", saveFileMan->getErrorDesc().c_str());
+ return kTestPassed;
+}
+
+SaveGameTestSuite::SaveGameTestSuite() {
+ addTest("OpeningSaveFile", &SaveGametests::testSaveLoadState, false);
+ addTest("RemovingSaveFile", &SaveGametests::testRemovingSavefile, false);
+ addTest("RenamingSaveFile", &SaveGametests::testRenamingSavefile, false);
+ addTest("ListingSaveFile", &SaveGametests::testListingSavefile, false);
+ addTest("VerifyErrorMessages", &SaveGametests::testErrorMessages, false);
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/savegame.h b/engines/testbed/savegame.h
new file mode 100644
index 0000000000..dc41ff9b65
--- /dev/null
+++ b/engines/testbed/savegame.h
@@ -0,0 +1,69 @@
+/* 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 TESTBED_SAVEGAME_H
+#define TESTBED_SAVEGAME_H
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+namespace SaveGametests {
+
+// Helper functions for SaveGame tests
+bool writeDataToFile(const char *fileName, const char *msg);
+bool readAndVerifyData(const char *fileName, const char *expected);
+// will contain function declarations for SaveGame tests
+TestExitStatus testSaveLoadState();
+TestExitStatus testRemovingSavefile();
+TestExitStatus testRenamingSavefile();
+TestExitStatus testListingSavefile();
+TestExitStatus testErrorMessages();
+// add more here
+
+} // End of namespace SaveGametests
+
+class SaveGameTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the SaveGameTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ SaveGameTestSuite();
+ ~SaveGameTestSuite() {}
+ const char *getName() const {
+ return "SaveGames";
+ }
+ const char *getDescription() const {
+ return "Saving Game state tests";
+ }
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_SAVEGAME_H
diff --git a/engines/testbed/sound.cpp b/engines/testbed/sound.cpp
new file mode 100644
index 0000000000..e256621553
--- /dev/null
+++ b/engines/testbed/sound.cpp
@@ -0,0 +1,280 @@
+/* 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 "sound/audiocd.h"
+#include "sound/softsynth/pcspk.h"
+
+#include "testbed/sound.h"
+
+namespace Testbed {
+
+enum {
+ kPlayChannel1 = 'pch1',
+ kPlayChannel2 = 'pch2',
+ kPlayChannel3 = 'pch3',
+ kPauseChannel1 = 'pac1',
+ kPauseChannel2 = 'pac2',
+ kPauseChannel3 = 'pac3'
+};
+
+SoundSubsystemDialog::SoundSubsystemDialog() : TestbedInteractionDialog(80, 60, 400, 170) {
+ _xOffset = 25;
+ _yOffset = 0;
+ Common::String text = "Sound Subsystem Tests: Test Mixing of Audio Streams.";
+ addText(350, 20, text, Graphics::kTextAlignCenter, _xOffset, 15);
+ addButton(200, 20, "Play Channel #1", kPlayChannel1);
+ addButton(200, 20, "Play Channel #2", kPlayChannel2);
+ addButton(200, 20, "Play Channel #3", kPlayChannel3);
+ addButton(50, 20, "Close", GUI::kCloseCmd, 160, 15);
+
+ _mixer = g_system->getMixer();
+
+ // the three streams to be mixed
+ Audio::PCSpeaker *s1 = new Audio::PCSpeaker();
+ Audio::PCSpeaker *s2 = new Audio::PCSpeaker();
+ Audio::PCSpeaker *s3 = new Audio::PCSpeaker();
+
+ s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+ s2->play(Audio::PCSpeaker::kWaveFormSine, 1200, -1);
+ s3->play(Audio::PCSpeaker::kWaveFormSine, 1400, -1);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_h1, s1);
+ _mixer->pauseHandle(_h1, true);
+
+ _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_h2, s2);
+ _mixer->pauseHandle(_h2, true);
+
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_h3, s3);
+ _mixer->pauseHandle(_h3, true);
+
+}
+
+
+void SoundSubsystemDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+
+ switch (cmd) {
+ case kPlayChannel1:
+ _buttonArray[0]->setLabel("Pause Channel #1");
+ _buttonArray[0]->setCmd(kPauseChannel1);
+ _mixer->pauseHandle(_h1, false);
+ break;
+ case kPlayChannel2:
+ _buttonArray[1]->setLabel("Pause Channel #2");
+ _buttonArray[1]->setCmd(kPauseChannel2);
+ _mixer->pauseHandle(_h2, false);
+ break;
+ case kPlayChannel3:
+ _buttonArray[2]->setLabel("Pause Channel #3");
+ _buttonArray[2]->setCmd(kPauseChannel3);
+ _mixer->pauseHandle(_h3, false);
+ break;
+ case kPauseChannel1:
+ _buttonArray[0]->setLabel("Play Channel #1");
+ _buttonArray[0]->setCmd(kPlayChannel1);
+ _mixer->pauseHandle(_h1, true);
+ break;
+ case kPauseChannel2:
+ _buttonArray[1]->setLabel("Play Channel #2");
+ _buttonArray[1]->setCmd(kPlayChannel2);
+ _mixer->pauseHandle(_h2, true);
+ break;
+ case kPauseChannel3:
+ _buttonArray[2]->setLabel("Play Channel #3");
+ _buttonArray[2]->setCmd(kPlayChannel3);
+ _mixer->pauseHandle(_h3, true);
+ break;
+ default:
+ _mixer->stopAll();
+ GUI::Dialog::handleCommand(sender, cmd, data);
+ }
+}
+
+TestExitStatus SoundSubsystem::playBeeps() {
+ Testsuite::clearScreen();
+ TestExitStatus passed = kTestPassed;
+ Common::String info = "Testing Sound Output by generating beeps\n"
+ "You should hear a left beep followed by a right beep\n";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Play Beeps\n");
+ return kTestSkipped;
+ }
+
+ Audio::PCSpeaker *speaker = new Audio::PCSpeaker();
+ Audio::Mixer *mixer = g_system->getMixer();
+ Audio::SoundHandle handle;
+ mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, speaker);
+
+ // Left Beep
+ Testsuite::writeOnScreen("Left Beep", Common::Point(0, 100));
+ mixer->setChannelBalance(handle, -127);
+ speaker->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+ g_system->delayMillis(500);
+ mixer->pauseHandle(handle, true);
+
+ if (Testsuite::handleInteractiveInput(" Were you able to hear the left beep? ", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! Left Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
+ passed = kTestFailed;
+ }
+
+ // Right Beep
+ Testsuite::writeOnScreen("Right Beep", Common::Point(0, 100));
+ mixer->setChannelBalance(handle, 127);
+ mixer->pauseHandle(handle, false);
+ g_system->delayMillis(500);
+ mixer->stopAll();
+
+ if (Testsuite::handleInteractiveInput("Were you able to hear the right beep?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! Right Beep couldn't be detected : Error with Mixer::setChannelBalance()\n");
+ passed = kTestFailed;
+ }
+ return passed;
+}
+
+TestExitStatus SoundSubsystem::mixSounds() {
+ Testsuite::clearScreen();
+ TestExitStatus passed = kTestPassed;
+ Common::String info = "Testing Mixer Output by generating multichannel sound output using PC speaker emulator.\n"
+ "The mixer should be able to play them simultaneously\n";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Mix Sounds\n");
+ return kTestSkipped;
+ }
+
+ SoundSubsystemDialog sDialog;
+ sDialog.runModal();
+ if (Testsuite::handleInteractiveInput("Was the mixer able to simultaneously play multiple channels?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! Multiple channels couldn't be played : Error with Mixer Class\n");
+ passed = kTestFailed;
+ }
+ return passed;
+}
+
+TestExitStatus SoundSubsystem::audiocdOutput() {
+ Testsuite::clearScreen();
+ TestExitStatus passed = kTestPassed;
+ Common::String info = "Testing AudioCD API implementation.\n"
+ "Here we have four tracks, we play them in order i.e 1-2-3-last.\n"
+ "The user should verify if the tracks were run in correct order or not.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : AudioCD API\n");
+ return kTestSkipped;
+ }
+
+ Common::Point pt(0, 100);
+ Testsuite::writeOnScreen("Playing the tracks of testCD in order i.e 1-2-3-last", pt);
+
+
+ // Play all tracks
+ for (int i = 1; i < 5; i++) {
+ AudioCD.play(i, 1, 0, 0);
+ while (AudioCD.isPlaying()) {
+ g_system->delayMillis(500);
+ Testsuite::writeOnScreen(Common::String::printf("Playing Now: track%02d", i), pt);
+ }
+ g_system->delayMillis(500);
+ }
+
+ Testsuite::clearScreen();
+ if (Testsuite::handleInteractiveInput("Were all the tracks played in order i.e 1-2-3-last ?", "Yes", "No", kOptionRight)) {
+ Testsuite::logPrintf("Error! Error in AudioCD.play() or probably sound files were not detected, try -d1 (debuglevel 1)\n");
+ passed = kTestFailed;
+ }
+
+ return passed;
+}
+
+TestExitStatus SoundSubsystem::sampleRates() {
+
+ Common::String info = "Testing Multiple Sample Rates.\n"
+ "Here we try to play sounds at three different sample rates.";
+
+ if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+ Testsuite::logPrintf("Info! Skipping test : Sample Rates\n");
+ return kTestSkipped;
+ }
+
+ TestExitStatus passed = kTestPassed;
+ Audio::Mixer *mixer = g_system->getMixer();
+
+ Audio::PCSpeaker *s1 = new Audio::PCSpeaker();
+ // Stream at half sampling rate
+ Audio::PCSpeaker *s2 = new Audio::PCSpeaker(s1->getRate() - 10000);
+ // Stream at twice sampling rate
+ Audio::PCSpeaker *s3 = new Audio::PCSpeaker(s1->getRate() + 10000);
+
+ s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+ s2->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+ s3->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1);
+
+ Audio::SoundHandle handle;
+ Common::Point pt(0, 100);
+
+ mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, s1);
+ Testsuite::writeOnScreen(Common::String::printf("Playing at smaple rate: %d", s1->getRate()), pt);
+ g_system->delayMillis(1000);
+ mixer->stopHandle(handle);
+ g_system->delayMillis(1000);
+
+ mixer->playStream(Audio::Mixer::kSpeechSoundType, &handle, s2);
+ Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s2->getRate()), pt);
+ g_system->delayMillis(1000);
+ mixer->stopHandle(handle);
+ g_system->delayMillis(1000);
+
+ mixer->playStream(Audio::Mixer::kSFXSoundType, &handle, s3);
+ Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s3->getRate()), pt);
+ g_system->delayMillis(1000);
+ mixer->stopHandle(handle);
+ g_system->delayMillis(1000);
+
+ Testsuite::clearScreen();
+ if (Testsuite::handleInteractiveInput("Was the mixer able to play beeps with variable sample rates?", "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! Error with variable sample rates\n");
+ passed = kTestFailed;
+ }
+
+ return passed;
+}
+
+SoundSubsystemTestSuite::SoundSubsystemTestSuite() {
+ addTest("SimpleBeeps", &SoundSubsystem::playBeeps, true);
+ addTest("MixSounds", &SoundSubsystem::mixSounds, true);
+
+ // Make audio-files discoverable
+ Common::FSNode gameRoot(ConfMan.get("path"));
+ if (gameRoot.exists()) {
+ SearchMan.addSubDirectoryMatching(gameRoot, "audiocd-files");
+ if (SearchMan.hasFile("track01.mp3") && SearchMan.hasFile("track02.mp3") && SearchMan.hasFile("track03.mp3") && SearchMan.hasFile("track04.mp3")) {
+ addTest("AudiocdOutput", &SoundSubsystem::audiocdOutput, true);
+ } else {
+ Testsuite::logPrintf("Warning! Skipping test AudioCD: Required data files missing, check game-dir/audiocd-files\n");
+ }
+ }
+ addTest("SampleRates", &SoundSubsystem::sampleRates, true);
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/sound.h b/engines/testbed/sound.h
new file mode 100644
index 0000000000..24dcf45b99
--- /dev/null
+++ b/engines/testbed/sound.h
@@ -0,0 +1,83 @@
+/* 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 TESTBED_SOUND_H
+#define TESTBED_SOUND_H
+
+#include "gui/dialog.h"
+#include "sound/mixer.h"
+#include "testbed/config.h"
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+class SoundSubsystemDialog : public TestbedInteractionDialog {
+public:
+ SoundSubsystemDialog();
+ ~SoundSubsystemDialog() {}
+ void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _h1, _h2, _h3;
+};
+
+namespace SoundSubsystem {
+
+// Helper functions for SoundSubsystem tests
+
+// will contain function declarations for SoundSubsystem tests
+TestExitStatus playBeeps();
+TestExitStatus mixSounds();
+TestExitStatus audiocdOutput();
+TestExitStatus sampleRates();
+}
+
+class SoundSubsystemTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the SoundSubsystemTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ SoundSubsystemTestSuite();
+ ~SoundSubsystemTestSuite() {}
+
+ const char *getName() const {
+ return "SoundSubsystem";
+ }
+
+ const char *getDescription() const {
+ return "Sound Subsystem";
+ }
+
+private:
+ bool _isTestDataFound;
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_SOUND_H
diff --git a/engines/testbed/template.h b/engines/testbed/template.h
new file mode 100644
index 0000000000..849d157a03
--- /dev/null
+++ b/engines/testbed/template.h
@@ -0,0 +1,67 @@
+/* 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 TESTBED_TEMPLATE_H
+#define TESTBED_TEMPLATE_H
+
+#include "testbed/testsuite.h"
+
+// This file can be used as template for header files of other newer testsuites.
+
+namespace Testbed {
+
+namespace XXXtests {
+
+// Helper functions for XXX tests
+
+// will contain function declarations for XXX tests
+// add more here
+
+} // End of namespace XXXtests
+
+class XXXTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the XXXTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ XXXTestSuite();
+ ~XXXTestSuite() {}
+ const char *getName() const {
+ return "Dummy Template";
+ }
+
+ const char *getDescription() const {
+ return "Some Arbit description";
+ }
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_TEMPLATE_H
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
new file mode 100644
index 0000000000..071fba8c2c
--- /dev/null
+++ b/engines/testbed/testbed.cpp
@@ -0,0 +1,192 @@
+/* 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/debug-channels.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+
+#include "engines/util.h"
+
+#include "testbed/events.h"
+#include "testbed/fs.h"
+#include "testbed/graphics.h"
+#include "testbed/midi.h"
+#include "testbed/misc.h"
+#include "testbed/savegame.h"
+#include "testbed/sound.h"
+#include "testbed/testbed.h"
+
+namespace Testbed {
+
+void TestbedExitDialog::init() {
+ _xOffset = 25;
+ _yOffset = 0;
+ Common::String text = "Thank you for using ScummVM testbed! Here are yor summarized results:";
+ addText(450, 20, text, Graphics::kTextAlignCenter, _xOffset, 15);
+ Common::Array<Common::String> strArray;
+ GUI::ListWidget::ColorList colors;
+
+ for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i != _testsuiteList.end(); ++i) {
+ strArray.push_back(Common::String::printf("%s :", (*i)->getDescription()));
+ colors.push_back(GUI::ThemeEngine::kFontColorNormal);
+ if ((*i)->isEnabled()) {
+ strArray.push_back(Common::String::printf("Passed: %d Failed: %d Skipped: %d", (*i)->getNumTestsPassed(), (*i)->getNumTestsFailed(), (*i)->getNumTestsSkipped()));
+ } else {
+ strArray.push_back("Skipped");
+ }
+ colors.push_back(GUI::ThemeEngine::kFontColorAlternate);
+ }
+
+ addList(0, _yOffset, 500, 200, strArray, &colors);
+ text = "More Details can be viewed in the Log file : " + ConfParams.getLogFilename();
+ addText(450, 20, text, Graphics::kTextAlignLeft, 0, 0);
+ if (ConfParams.getLogDirectory().size()) {
+ text = "Directory : " + ConfParams.getLogDirectory();
+ } else {
+ text = "Directory : .";
+ }
+ addText(500, 20, text, Graphics::kTextAlignLeft, 0, 0);
+ _yOffset += 5;
+ addButtonXY(_xOffset + 80, _yOffset, 120, 24, "Rerun test suite", kCmdRerunTestbed);
+ addButtonXY(_xOffset + 240, _yOffset, 60, 24, "Close", GUI::kCloseCmd);
+}
+
+void TestbedExitDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+ switch (cmd) {
+ case kCmdRerunTestbed :
+ ConfParams.setRerunFlag(true);
+ cmd = GUI::kCloseCmd;
+ default:
+ GUI::Dialog::handleCommand(sender, cmd, data);
+ }
+}
+
+bool TestbedEngine::hasFeature(EngineFeature f) const {
+ return (f == kSupportsRTL) ? true : false;
+}
+
+TestbedEngine::TestbedEngine(OSystem *syst)
+ : Engine(syst) {
+ // Put your engine in a sane state, but do nothing big yet;
+ // in particular, do not load data from files; rather, if you
+ // need to do such things, do them from init().
+
+ // Do not initialize graphics here
+
+ // However this is the place to specify all default directories
+ // Put game-data dir in search path
+ Common::FSNode gameRoot(ConfMan.get("path"));
+ if (gameRoot.exists()) {
+ SearchMan.addDirectory(gameRoot.getDisplayName(), gameRoot);
+ }
+
+ DebugMan.addDebugChannel(kTestbedLogOutput, "LOG", "Log of test results generated by testbed");
+ DebugMan.addDebugChannel(kTestbedEngineDebug, "Debug", "Engine-specific debug statements");
+ DebugMan.enableDebugChannel("LOG");
+
+ // Initialize testsuites here
+ // GFX
+ Testsuite *ts = new GFXTestSuite();
+ _testsuiteList.push_back(ts);
+ // FS
+ ts = new FSTestSuite();
+ _testsuiteList.push_back(ts);
+ // Savegames
+ ts = new SaveGameTestSuite();
+ _testsuiteList.push_back(ts);
+ // Misc.
+ ts = new MiscTestSuite();
+ _testsuiteList.push_back(ts);
+ // Events
+ ts = new EventTestSuite();
+ _testsuiteList.push_back(ts);
+ // Sound
+ ts = new SoundSubsystemTestSuite();
+ _testsuiteList.push_back(ts);
+ // Midi
+ ts = new MidiTestSuite();
+ _testsuiteList.push_back(ts);
+}
+
+TestbedEngine::~TestbedEngine() {
+ ConfParams.deleteWriteStream();
+ // Remove all of our debug levels here
+ DebugMan.clearAllDebugChannels();
+
+ for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i != _testsuiteList.end(); ++i) {
+ delete (*i);
+ }
+}
+
+void TestbedEngine::invokeTestsuites(TestbedConfigManager &cfMan) {
+ Common::Array<Testsuite *>::const_iterator iter;
+ uint count = 1;
+ Common::Point pt = Testsuite::getDisplayRegionCoordinates();
+ int numSuitesEnabled = cfMan.getNumSuitesEnabled();
+
+ for (iter = _testsuiteList.begin(); iter != _testsuiteList.end(); iter++) {
+ if (shouldQuit()) {
+ return;
+ }
+ (*iter)->reset();
+ if ((*iter)->isEnabled()) {
+ Testsuite::updateStats("Testsuite", (*iter)->getName(), count++, numSuitesEnabled, pt);
+ (*iter)->execute();
+ }
+ }
+}
+
+Common::Error TestbedEngine::run() {
+ // Initialize graphics using following:
+ initGraphics(320, 200, false);
+
+ // As of now we are using GUI::MessageDialog for interaction, Test if it works.
+ // interactive mode could also be modified by a config parameter "non-interactive=1"
+ // TODO: Implement that
+
+ TestbedConfigManager cfMan(_testsuiteList, "testbed.config");
+
+ // Keep running if rerun requested
+
+ do {
+ Testsuite::clearEntireScreen();
+ cfMan.selectTestsuites();
+ // Init logging
+ ConfParams.initLogging(true);
+ invokeTestsuites(cfMan);
+ // Check if user wanted to exit.
+ if (Engine::shouldQuit()) {
+ return Common::kNoError;
+ }
+
+ TestbedExitDialog tbDialog(_testsuiteList);
+ tbDialog.init();
+ tbDialog.run();
+
+ } while (ConfParams.isRerunRequired());
+
+ return Common::kNoError;
+}
+
+} // End of namespace Testbed
diff --git a/engines/testbed/testbed.h b/engines/testbed/testbed.h
new file mode 100644
index 0000000000..e0feb52ff5
--- /dev/null
+++ b/engines/testbed/testbed.h
@@ -0,0 +1,77 @@
+/* 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 TESTBED_H
+#define TESTBED_H
+
+#include "engines/engine.h"
+
+#include "gui/options.h"
+
+#include "testbed/config.h"
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+class TestbedConfigManager;
+
+enum {
+ kTestbedLogOutput = 1 << 0,
+ kTestbedEngineDebug = 1 << 2,
+ kCmdRerunTestbed = 'crtb'
+};
+
+class TestbedEngine : public Engine {
+public:
+ TestbedEngine(OSystem *syst);
+ ~TestbedEngine();
+
+ virtual Common::Error run();
+
+ /**
+ * Invokes configured testsuites.
+ */
+ void invokeTestsuites(TestbedConfigManager &cfMan);
+
+ bool hasFeature(EngineFeature f) const;
+
+private:
+ Common::Array<Testsuite *> _testsuiteList;
+};
+
+class TestbedExitDialog : public TestbedInteractionDialog {
+public:
+ TestbedExitDialog(Common::Array<Testsuite *> &testsuiteList) : TestbedInteractionDialog(80, 40, 500, 330),
+ _testsuiteList(testsuiteList) {}
+ ~TestbedExitDialog() {}
+ void init();
+ void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+ void run() { runModal(); }
+private:
+ Common::Array<Testsuite *> &_testsuiteList;
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_H
diff --git a/engines/testbed/testsuite.cpp b/engines/testbed/testsuite.cpp
new file mode 100644
index 0000000000..8cb9ffe309
--- /dev/null
+++ b/engines/testbed/testsuite.cpp
@@ -0,0 +1,333 @@
+/* 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/events.h"
+#include "common/stream.h"
+
+#include "graphics/fontman.h"
+#include "graphics/surface.h"
+
+#include "gui/message.h"
+
+#include "testbed/graphics.h"
+#include "testbed/testbed.h"
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+void Testsuite::logPrintf(const char *fmt, ...) {
+ // Assuming log message size to be not greater than STRINGBUFLEN i.e 256
+ char buffer[STRINGBUFLEN];
+ va_list vl;
+ va_start(vl, fmt);
+ vsnprintf(buffer, STRINGBUFLEN, fmt, vl);
+ va_end(vl);
+ Common::WriteStream *ws = ConfigParams::instance().getLogWriteStream();
+
+ if (ws) {
+ ws->writeString(buffer);
+ ws->flush();
+ debugCN(kTestbedLogOutput, "%s", buffer);
+ } else {
+ debugCN(kTestbedLogOutput, "%s", buffer);
+ }
+}
+
+void Testsuite::logDetailedPrintf(const char *fmt, ...) {
+ // Assuming log message size to be not greater than STRINGBUFLEN i.e 256
+ // Messages with this function would only be displayed if -d1 is specified on command line
+ char buffer[STRINGBUFLEN];
+ va_list vl;
+ va_start(vl, fmt);
+ vsnprintf(buffer, STRINGBUFLEN, fmt, vl);
+ va_end(vl);
+ Common::WriteStream *ws = ConfigParams::instance().getLogWriteStream();
+
+ if (ws) {
+ ws->writeString(buffer);
+ ws->flush();
+ debugCN(1, kTestbedLogOutput, "%s", buffer);
+ } else {
+ debugCN(1, kTestbedLogOutput, "%s", buffer);
+ }
+}
+
+Testsuite::Testsuite() {
+ _numTestsPassed = 0;
+ _numTestsExecuted = 0;
+ _numTestsSkipped = 0;
+ _toQuit = kLoopNormal;
+ // Initially all testsuites are enabled, disable them by calling enableTestSuite(name, false)
+ _isTsEnabled = true;
+ // Set custom color for progress bar
+ GFXTestSuite::setCustomColor(0, 0, 0);
+}
+
+Testsuite::~Testsuite() {
+ for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) {
+ delete (*i);
+ }
+}
+
+void Testsuite::reset() {
+ _numTestsPassed = 0;
+ _numTestsExecuted = 0;
+ _toQuit = kLoopNormal;
+ for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) {
+ (*i)->passed = false;
+ }
+}
+
+void Testsuite::genReport() const {
+ logPrintf("\n");
+ logPrintf("Consolidating results...\n");
+ logPrintf("Subsystem: %s ", getName());
+ logPrintf("(Tests Executed: %d)\n", _numTestsExecuted);
+ logPrintf("Passed: %d ", _numTestsPassed);
+ logPrintf("Skipped: %d ", _numTestsSkipped);
+ logPrintf("Failed: %d\n", getNumTestsFailed());
+ logPrintf("\n");
+}
+
+bool Testsuite::handleInteractiveInput(const Common::String &textToDisplay, const char *opt1, const char *opt2, OptionSelected result) {
+ GUI::MessageDialog prompt(textToDisplay, opt1, opt2);
+ return prompt.runModal() == result ? true : false;
+}
+
+void Testsuite::displayMessage(const Common::String &textToDisplay, const char *defaultButton, const char *altButton) {
+ GUI::MessageDialog prompt(textToDisplay, defaultButton);
+ prompt.runModal();
+}
+
+Common::Rect Testsuite::writeOnScreen(const Common::String &textToDisplay, const Common::Point &pt, bool flag) {
+ const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont));
+ uint fillColor = kColorBlack;
+ uint textColor = kColorWhite;
+
+ Graphics::Surface *screen = g_system->lockScreen();
+
+ int height = font.getFontHeight();
+ int width = screen->w;
+
+ Common::Rect rect(pt.x, pt.y, pt.x + width, pt.y + height);
+
+ if (flag) {
+ Graphics::PixelFormat pf = g_system->getScreenFormat();
+ fillColor = pf.RGBToColor(0, 0, 0);
+ textColor = pf.RGBToColor(255, 255, 255);
+ }
+
+ screen->fillRect(rect, fillColor);
+ font.drawString(screen, textToDisplay, rect.left, rect.top, screen->w, textColor, Graphics::kTextAlignCenter);
+
+ g_system->unlockScreen();
+ g_system->updateScreen();
+
+ return rect;
+}
+
+void Testsuite::clearScreen(const Common::Rect &rect) {
+ Graphics::Surface *screen = g_system->lockScreen();
+
+ screen->fillRect(rect, kColorBlack);
+
+ g_system->unlockScreen();
+ g_system->updateScreen();
+}
+
+void Testsuite::clearScreen() {
+ int numBytesPerLine = g_system->getWidth() * g_system->getScreenFormat().bytesPerPixel;
+ int height = getDisplayRegionCoordinates().y;
+
+ // Don't clear test info display region
+ int size = height * numBytesPerLine;
+ byte *buffer = new byte[size];
+ memset(buffer, 0, size);
+ g_system->copyRectToScreen(buffer, numBytesPerLine, 0, 0, g_system->getWidth(), height);
+ g_system->updateScreen();
+ delete[] buffer;
+}
+
+void Testsuite::clearScreen(bool flag) {
+ Graphics::Surface *screen = g_system->lockScreen();
+ uint fillColor = kColorBlack;
+
+ if (flag) {
+ fillColor = g_system->getScreenFormat().RGBToColor(0, 0, 0);
+ }
+
+ screen->fillRect(Common::Rect(0, 0, g_system->getWidth(), g_system->getHeight()), fillColor);
+
+ g_system->unlockScreen();
+ g_system->updateScreen();
+}
+
+void Testsuite::addTest(const Common::String &name, InvokingFunction f, bool isInteractive) {
+ Test *featureTest = new Test(name, f, isInteractive);
+ _testsToExecute.push_back(featureTest);
+}
+
+int Testsuite::getNumTestsEnabled() {
+ int count = 0;
+ Common::Array<Test *>::const_iterator iter;
+
+ if (!isEnabled()) {
+ return 0;
+ }
+
+ for (iter = _testsToExecute.begin(); iter != _testsToExecute.end(); iter++) {
+ if ((*iter)->enabled) {
+ count++;
+ }
+ }
+ return count;
+}
+
+uint Testsuite::parseEvents() {
+ uint startTime = g_system->getMillis();
+ uint end = startTime + kEventHandlingTime;
+ do {
+ Common::Event ev;
+ while (g_system->getEventManager()->pollEvent(ev)) {
+ switch (ev.type) {
+ case Common::EVENT_KEYDOWN:
+ if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) {
+ return kSkipNext;
+ }
+ break;
+ case Common::EVENT_QUIT:
+ case Common::EVENT_RTL:
+ return kEngineQuit;
+ break;
+ default:
+ break;
+ }
+ }
+ g_system->delayMillis(10);
+ startTime = g_system->getMillis();
+ } while (startTime <= end);
+
+ return kLoopNormal;
+}
+
+void Testsuite::updateStats(const char *prefix, const char *info, uint testNum, uint numTests, Common::Point pt) {
+ Common::String text = Common::String::printf(" Running %s: %s (%d of %d) ", prefix, info, testNum, numTests);
+ writeOnScreen(text, pt);
+ uint barColor = kColorSpecial;
+ // below the text a rectangle denoting the progress in the testsuite can be drawn.
+ int separation = getLineSeparation();
+ pt.y += separation;
+ int wRect = 200;
+ int lRect = 7;
+ pt.x = g_system->getWidth() / 2 - 100;
+ byte *buffer = new byte[lRect * wRect];
+ memset(buffer, 0, sizeof(byte) * lRect * wRect);
+
+ int wShaded = (int) (wRect * (((float)testNum) / numTests));
+
+ // draw the boundary
+ memset(buffer, barColor, sizeof(byte) * wRect);
+ memset(buffer + (wRect * (lRect - 1)) , barColor, sizeof(byte) * wRect);
+
+ for (int i = 0; i < lRect; i++) {
+ for (int j = 0; j < wRect; j++) {
+ if (j < wShaded) {
+ buffer[i * wRect + j] = barColor;
+ }
+ }
+ buffer[i * wRect + 0] = barColor;
+ buffer[i * wRect + wRect - 1] = barColor;
+ }
+ g_system->copyRectToScreen(buffer, wRect, pt.x, pt.y, wRect, lRect);
+ g_system->updateScreen();
+ delete[] buffer;
+}
+
+bool Testsuite::enableTest(const Common::String &testName, bool toEnable) {
+ for (uint i = 0; i < _testsToExecute.size(); i++) {
+ if (_testsToExecute[i]->featureName.equalsIgnoreCase(testName)) {
+ _testsToExecute[i]->enabled = toEnable;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Testsuite::execute() {
+ // Main Loop for a testsuite
+
+ uint count = 0;
+ Common::Point pt = getDisplayRegionCoordinates();
+ pt.y += getLineSeparation();
+ int numEnabledTests = getNumTestsEnabled();
+
+ for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) {
+ if (!(*i)->enabled) {
+ logPrintf("Info! Skipping Test: %s, Skipped by configuration.\n", ((*i)->featureName).c_str());
+ _numTestsSkipped++;
+ continue;
+ }
+
+ if((*i)->isInteractive && !ConfParams.isSessionInteractive()) {
+ logPrintf("Info! Skipping Test: %s, non-interactive environment is selected\n", ((*i)->featureName).c_str());
+ _numTestsSkipped++;
+ continue;
+ }
+
+ logPrintf("Info! Executing Test: %s\n", ((*i)->featureName).c_str());
+ updateStats("Test", ((*i)->featureName).c_str(), count++, numEnabledTests, pt);
+
+ // Run the test and capture exit status.
+ TestExitStatus eStatus = (*i)->driver();
+ if (kTestPassed == eStatus) {
+ logPrintf("Result: Passed\n");
+ _numTestsExecuted++;
+ _numTestsPassed++;
+ } else if (kTestSkipped == eStatus){
+ logPrintf("Result: Skipped\n");
+ _numTestsSkipped++;
+ } else {
+ _numTestsExecuted++;
+ logPrintf("Result: Failed\n");
+ }
+
+ updateStats("Test", ((*i)->featureName).c_str(), count, numEnabledTests, pt);
+ // TODO: Display a screen here to user with details of upcoming test, he can skip it or Quit or RTL
+ // Check if user wants to quit/RTL/Skip next test by parsing events.
+ // Quit directly if explicitly requested
+
+ if (Engine::shouldQuit()) {
+ _toQuit = kEngineQuit;
+ genReport();
+ return;
+ }
+
+ _toQuit = parseEvents();
+ }
+ genReport();
+}
+
+} // End of namespace Testebed
diff --git a/engines/testbed/testsuite.h b/engines/testbed/testsuite.h
new file mode 100644
index 0000000000..a738f40764
--- /dev/null
+++ b/engines/testbed/testsuite.h
@@ -0,0 +1,192 @@
+/* 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 TESTBED_TESTSUITE_H
+#define TESTBED_TESTSUITE_H
+
+#include "common/system.h"
+#include "common/str.h"
+#include "common/array.h"
+
+#include "graphics/fontman.h"
+
+#include "testbed/config-params.h"
+
+namespace Testbed {
+
+enum {
+ kColorBlack = 0,
+ kColorWhite = 1,
+ kColorCustom = 2,
+ kColorSpecial = 5 ///< some random number
+};
+
+enum OptionSelected {
+ kOptionLeft = 1,
+ kOptionRight = 0
+};
+
+enum {
+ kEngineQuit = 0,
+ kSkipNext = 1,
+ kLoopNormal = 2,
+ // Event handling time,(in ms) used in parseEvent()
+ kEventHandlingTime = 50
+};
+
+enum TestExitStatus {
+ kTestPassed = 0,
+ kTestSkipped,
+ kTestFailed
+};
+
+typedef TestExitStatus (*InvokingFunction)();
+
+/**
+ * This represents a feature to be tested
+ */
+
+struct Test {
+ Test(Common::String name, InvokingFunction f, bool interactive) : featureName(name) {
+ driver = f;
+ enabled = true;
+ passed = false;
+ isInteractive = interactive;
+ }
+ const Common::String featureName; ///< Name of feature to be tested
+ InvokingFunction driver; ///< Pointer to the function that will invoke this feature test
+ bool enabled; ///< Decides whether or not this test is to be executed
+ bool passed; ///< Collects and stores result of this feature test
+ bool isInteractive; ///< Decides if the test is interactive or not, An interactive testsuite may have non-interactive tests, hence this change.
+};
+
+
+/**
+ * The basic Testsuite class
+ * All the other testsuites would inherit it and override its virtual methods
+ */
+
+class Testsuite {
+public:
+ Testsuite();
+ virtual ~Testsuite();
+ int getNumTests() const { return _testsToExecute.size(); }
+ int getNumTestsPassed() const { return _numTestsPassed; }
+ int getNumTestsSkipped() const { return _numTestsSkipped; }
+ int getNumTestsFailed() const { return _numTestsExecuted - _numTestsPassed; }
+ void genReport() const;
+ bool isEnabled() const { return _isTsEnabled; }
+ virtual void enable(bool flag) {
+ _isTsEnabled = flag;
+ }
+ bool enableTest(const Common::String &testName, bool enable);
+ void reset();
+
+ /**
+ * Prompts for User Input in form of "Yes" or "No" for interactive tests
+ * e.g: "Is this like you expect?" "Yes" or "No"
+ *
+ * @param textToDisplay Display text
+ * @return true if "Yes" false otherwise
+ */
+ static bool handleInteractiveInput(const Common::String &textToDisplay, const char *opt1 = "Yes", const char *opt2 = "No", OptionSelected result = kOptionLeft);
+
+ static void displayMessage(const Common::String &textToDisplay, const char *defaultButton = "OK", const char *altButton = 0);
+ static Common::Rect writeOnScreen(const Common::String &textToDisplay, const Common::Point &pt, bool flag = false);
+ static void clearScreen(const Common::Rect &rect);
+ static void clearEntireScreen() {
+ const int width = g_system->getWidth();
+ const int height = g_system->getHeight();
+ Common::Rect r(0, 0, width, height);
+ clearScreen(r);
+ }
+ static void clearScreen();
+ static void clearScreen(bool flag);
+
+ /**
+ * Adds a test to the list of tests to be executed
+ *
+ * @param name the string description of the test, for display purposes
+ * @param f pointer to the function that invokes this test
+ * @param isInteractive decides if the test is to be executed in interactive mode/ default true
+ */
+ void addTest(const Common::String &name, InvokingFunction f, bool isInteractive = true);
+
+ /**
+ * The driver function for the testsuite
+ * All code should go in here.
+ */
+ virtual void execute();
+ static uint parseEvents();
+
+ virtual const char *getName() const = 0;
+ virtual const char *getDescription() const = 0;
+
+ static void logPrintf(const char *s, ...) GCC_PRINTF(1, 2);
+ static void logDetailedPrintf(const char *s, ...) GCC_PRINTF(1, 2);
+
+ // Progress bar (Information Display) related methods.
+ /**
+ * Display region is in the bottom. Probably 1/4th of the game screen.
+ * It contains:
+ * 1) Information about executing testsuite.
+ * 2) Total progress within this testsuite.
+ * 3) Total overall progress in the number of testsuites
+ */
+
+ static Common::Point getDisplayRegionCoordinates() {
+ Common::Point pt(0, 0);
+ // start from bottom
+ pt.y = g_system->getHeight();
+ // Will Contain 3 lines
+ pt.y -= (FontMan.getFontByUsage(ConfParams.getCurrentFontUsageType())->getFontHeight() * 3 + 15); // Buffer of 5 pixels per line
+ return pt;
+ }
+
+ static uint getLineSeparation() {
+ return FontMan.getFontByUsage(ConfParams.getCurrentFontUsageType())->getFontHeight() + 5;
+ }
+
+ static void updateStats(const char *prefix, const char *info, uint numTests, uint testNum, Common::Point pt);
+ const Common::Array<Test *>& getTestList() { return _testsToExecute; }
+ int getNumTestsEnabled();
+
+protected:
+ Common::Array<Test *> _testsToExecute; ///< List of tests to be executed
+ int _numTestsPassed; ///< Number of tests passed
+ int _numTestsExecuted; ///< Number of tests executed
+ int _numTestsSkipped;
+ bool _isTsEnabled;
+
+private:
+
+ /**
+ * Used from the code to decide if the engine needs to exit
+ */
+ uint _toQuit;
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_TESTSUITE_H
diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp
index b13de103c0..2077789b9c 100644
--- a/engines/tinsel/bmv.cpp
+++ b/engines/tinsel/bmv.cpp
@@ -66,13 +66,7 @@ namespace Tinsel {
#define PREFETCH (NUM_SLOTS/2) // For initial test
-#ifndef _Windows
-//#define ADVANCE_SOUND 12 // 1 second
-#define ADVANCE_SOUND 18 // 1 1/2 second
-//#define MAX_ADVANCE_SOUND 36 // 3 seconds
-#else
#define ADVANCE_SOUND 18 // 1 1/2 seconds
-#endif
#define SUBSEQUENT_SOUND 6 // 1/2 second
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
index b467cc613e..a2a32d2e13 100644
--- a/engines/tinsel/detection_tables.h
+++ b/engines/tinsel/detection_tables.h
@@ -394,7 +394,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI,
TINSEL_V1,
},
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index cb246bc8b3..0901cd08b8 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -69,42 +69,8 @@ static SOUND_BUFFER midiBuffer = { 0, 0 };
static SCNHANDLE currentMidi = 0;
static bool currentLoop = false;
-static const SCNHANDLE midiOffsetsGRAVersion[] = {
- 4, 4534, 14298, 18828, 23358, 38888, 54418, 57172, 59926, 62450,
- 62952, 67482, 72258, 74538, 79314, 87722, 103252, 115176, 127100, 127898,
- 130256, 132614, 134972, 137330, 139688, 150196, 152554, 154912, 167422, 174762,
- 182102, 194612, 198880, 199536, 206128, 206380, 216372, 226364, 235676, 244988,
- 249098, 249606, 251160, 252714, 263116, 268706, 274296, 283562, 297986, 304566,
- 312028, 313524, 319192, 324860, 331772, 336548, 336838, 339950, 343062, 346174,
- 349286, 356246, 359358, 360434, 361510, 369966, 374366, 382822, 384202, 394946,
- 396022, 396730, 399524, 401020, 403814, 418364, 419466, 420568, 425132, 433540,
- 434384, 441504, 452132, 462760, 472804, 486772, 491302, 497722, 501260, 507680,
- 509726, 521858, 524136, 525452, 533480, 538236, 549018, 559870, 564626, 565306,
- 566734, 567616, 570144, 574102, 574900, 582518, 586350, 600736, 604734, 613812,
- 616566, 619626, 623460, 627294, 631128, 634188, 648738, 663288, 667864, 681832,
- 682048, 683014, 688908, 689124, 698888, 708652, 718416, 728180, 737944, 747708,
- 752238, 765522, 766554, 772944, 774546, 776148, 776994, 781698, 786262, 789016,
- 794630, 796422, 798998
-};
-
-static const SCNHANDLE midiOffsetsSCNVersion[] = {
- 4, 4504, 11762, 21532, 26070, 28754, 33254, 40512, 56310, 72108,
- 74864, 77620, 80152, 80662, 85200, 89982, 92268, 97050, 105466, 121264,
- 133194, 145124, 145928, 148294, 150660, 153026, 155392, 157758, 168272, 170638,
- 173004, 185522, 192866, 200210, 212728, 217000, 217662, 224254, 224756, 234754,
- 244752, 245256, 245950, 255256, 264562, 268678, 269192, 270752, 272312, 282712,
- 288312, 293912, 303186, 317624, 324210, 331680, 333208, 338884, 344560, 351478,
- 356262, 356552, 359670, 362788, 365906, 369024, 376014, 379132, 380214, 381296,
- 389758, 394164, 402626, 404012, 414762, 415844, 416552, 419352, 420880, 423680,
- 438236, 439338, 440440, 445010, 453426, 454276, 461398, 472032, 482666, 492716,
- 506690, 511226, 517654, 521198, 527626, 529676, 541814, 546210, 547532, 555562,
- 560316, 571104, 581962, 586716, 587402, 588836, 589718, 592246, 596212, 597016,
- 604636, 608474, 622862, 626860, 635944, 638700, 641456, 645298, 649140, 652982,
- 655738, 670294, 684850, 689432, 703628, 703850, 704816, 706350, 706572, 716342,
- 726112, 735882, 745652, 755422, 765192, 774962, 784732, 794502, 804272, 814042,
- 823812, 832996, 846286, 847324, 853714, 855324, 856934, 857786, 862496, 867066,
- 869822, 875436, 877234, 879818
-};
+// We allocate 155 entries because that's the maximum, used in the SCN version
+static SCNHANDLE midiOffsets[155];
static const int enhancedAudioGRAVersion[] = {
1, 2, 1, 1, 3, 3, 4, 4, 5, 6, // 1-10
@@ -139,34 +105,41 @@ static const int enhancedAudioSCNVersion[] = {
77, 78, 79, 80, 4, 4, 82, 83, 77, 4, // 111-120
84, 85, 86, 3124, 88, 89, 90, 88, 2, 2, // 121-130
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 131-140
- 3141, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150
+ 3142, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150
97, 98, 99, 99 // 151-154
};
+// TODO. This mapping is wrong
+static const int enhancedAudioSCNVersionALT[] = {
+ 301, 302, 2, 1, 1, 301, 302, 3, 3, 4, // 1-10
+ 4, 5, 6, 1, 7, 8, 9, 10, 8, 11, // 11-20
+ 11, 12, 13, 13, 13, 13, 13, 14, 13, 13, // 21-30
+ 15, 16, 17, 15, 18, 19, 20, 338, 21, 21, // 31-40
+ 341, 342, 22, 22, 23, 24, 25, 26, 27, 28, // 41-50
+ 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, // 51-60
+ 38, 39, 39, 39, 39, 40, 39, 41, 41, 42, // 61-70
+ 43, 42, 44, 45, 41, 46, 48, 47, 48, 49, // 71-80
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, // 81-90
+ 60, 61, 62, 63, 61, 64, 65, 66, 67, 68, // 91-100
+ 69, 70, 68, 71, 72, 73, 74, 75, 12, 76, // 101-110
+ 77, 78, 79, 80, 4, 4, 82, 83, 77, 4, // 111-120
+ 84, 85, 86, 3124, 88, 89, 90, 88, 2, 2, // 121-130
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 131-140
+ 3142, 91, 92, 93, 94, 94, 95, 96, 52, 4, // 141-150
+ 97, 98, 99 // 151-153
+};
+
int GetTrackNumber(SCNHANDLE hMidi) {
- if (_vm->getFeatures() & GF_SCNFILES) {
- for (int i = 0; i < ARRAYSIZE(midiOffsetsSCNVersion); i++) {
- if (midiOffsetsSCNVersion[i] == hMidi)
- return i;
- }
- } else {
- for (int i = 0; i < ARRAYSIZE(midiOffsetsGRAVersion); i++) {
- if (midiOffsetsGRAVersion[i] == hMidi)
- return i;
- }
- }
+ for (int i = 0; i < ARRAYSIZE(midiOffsets); i++)
+ if (midiOffsets[i] == hMidi)
+ return i;
return -1;
}
SCNHANDLE GetTrackOffset(int trackNumber) {
- if (_vm->getFeatures() & GF_SCNFILES) {
- assert(trackNumber < ARRAYSIZE(midiOffsetsSCNVersion));
- return midiOffsetsSCNVersion[trackNumber];
- } else {
- assert(trackNumber < ARRAYSIZE(midiOffsetsGRAVersion));
- return midiOffsetsGRAVersion[trackNumber];
- }
+ assert(trackNumber < ARRAYSIZE(midiOffsets));
+ return midiOffsets[trackNumber];
}
/**
@@ -184,7 +157,11 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
if (TinselV1PSX) return false;
if (_vm->_config->_musicVolume != 0) {
- SetMidiVolume(_vm->_config->_musicVolume);
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
}
// the index and length of the last tune loaded
@@ -198,7 +175,9 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
int trackNumber = GetTrackNumber(dwFileOffset);
int track = 0;
if (trackNumber >= 0) {
- if (_vm->getFeatures() & GF_SCNFILES)
+ if (_vm->getFeatures() & GF_ALT_MIDI)
+ track = enhancedAudioSCNVersionALT[trackNumber];
+ else if (_vm->getFeatures() & GF_SCNFILES)
track = enhancedAudioSCNVersion[trackNumber];
else
track = enhancedAudioGRAVersion[trackNumber];
@@ -383,6 +362,34 @@ void OpenMidiFiles() {
}
}
+ // Now scan through the contents of the MIDI file to find the offset
+ // of each individual track, in order to create a mapping from MIDI
+ // offset to track number, for the enhanced MIDI soundtrack.
+ // The first song is always at position 4. The subsequent ones are
+ // calculated dynamically.
+ uint32 curOffset = 4;
+ uint32 curTrack = 0;
+ uint32 songLength = 0;
+
+ // Init
+ for (int i = 0; i < ARRAYSIZE(midiOffsets); i++)
+ midiOffsets[i] = 0;
+
+ while (!midiStream.eos() && !midiStream.err()) {
+ if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
+ break;
+
+ assert(curTrack < ARRAYSIZE(midiOffsets));
+ midiOffsets[curTrack] = curOffset + (4 * curTrack);
+ //printf("%d: %d\n", curTrack, midiOffsets[curTrack]);
+
+ songLength = midiStream.readUint32LE();
+ curOffset += songLength;
+ midiStream.skip(songLength);
+
+ curTrack++;
+ }
+
midiStream.close();
}
@@ -963,8 +970,12 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
currentLoop = Loop;
if (_vm->_config->_musicVolume != 0 && Loop) {
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
PlayMidiSequence(currentMidi, true);
- SetMidiVolume(_vm->_config->_musicVolume);
+ SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
}
}
@@ -978,29 +989,21 @@ void dumpMusic() {
int outFileSize = 0;
char buffer[20000];
- int total = (_vm->getFeatures() & GF_SCNFILES) ?
- ARRAYSIZE(midiOffsetsSCNVersion) :
- ARRAYSIZE(midiOffsetsGRAVersion);
+ const int total = 155; // maximum (SCN version)
for (int i = 0; i < total; i++) {
+ if (midiOffsets[i] == 0)
+ break;
+
sprintf(outName, "track%03d.xmi", i + 1);
outFile.open(outName);
- if (_vm->getFeatures() & GF_SCNFILES) {
- if (i < total - 1)
- outFileSize = midiOffsetsSCNVersion[i + 1] - midiOffsetsSCNVersion[i] - 4;
- else
- outFileSize = midiFile.size() - midiOffsetsSCNVersion[i] - 4;
-
- midiFile.seek(midiOffsetsSCNVersion[i] + 4, SEEK_SET);
- } else {
- if (i < total - 1)
- outFileSize = midiOffsetsGRAVersion[i + 1] - midiOffsetsGRAVersion[i] - 4;
- else
- outFileSize = midiFile.size() - midiOffsetsGRAVersion[i] - 4;
+ if (i < total - 1)
+ outFileSize = midiOffsets[i + 1] - midiOffsets[i] - 4;
+ else
+ outFileSize = midiFile.size() - midiOffsets[i] - 4;
- midiFile.seek(midiOffsetsGRAVersion[i] + 4, SEEK_SET);
- }
+ midiFile.seek(midiOffsets[i] + 4, SEEK_SET);
assert(outFileSize < 20000);
midiFile.read(buffer, outFileSize);
diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp
index ce2ed51d09..6e8e736e14 100644
--- a/engines/tinsel/sound.cpp
+++ b/engines/tinsel/sound.cpp
@@ -34,6 +34,7 @@
#include "tinsel/sysvar.h"
#include "tinsel/background.h"
+#include "common/config-manager.h"
#include "common/endian.h"
#include "common/file.h"
#include "common/system.h"
@@ -128,9 +129,13 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// FIXME: Should set this in a different place ;)
- _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : _vm->_config->_soundVolume);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
- _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : _vm->_config->_voiceVolume);
Audio::AudioStream *sampleStream = 0;
@@ -318,9 +323,13 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p
}
// FIXME: Should set this in a different place ;)
- _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, mute ? 0 : _vm->_config->_soundVolume);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
- _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, mute ? 0 : _vm->_config->_voiceVolume);
curChan->sampleNum = id;
curChan->subSample = sub;
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index dc706c82d9..f16b5f9100 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -145,7 +145,6 @@ void KeyboardProcess(CORO_PARAM, const void *) {
// Get the next keyboard event off the stack
Common::Event evt = _vm->_keypresses.front();
_vm->_keypresses.pop_front();
- const Common::Point mousePos = _vm->getMousePosition();
// Switch for special keys
switch (evt.kbd.keycode) {
@@ -282,7 +281,7 @@ static void SingleLeftProcess(CORO_PARAM, const void *param) {
} while (DwGetCurrentTime() < _ctx->endTicks);
if (GetProvNotProcessed()) {
- Common::Point clickPos = *(Common::Point *)param;
+ const Common::Point clickPos = *(const Common::Point *)param;
PlayerEvent(PLR_WALKTO, clickPos);
}
@@ -833,8 +832,7 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
DebugMan.addDebugChannel(kTinselDebugMusic, "music", "Music debugging");
// Setup mixer
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ syncSoundSettings();
// Add DW2 subfolder to search path in case user is running directly from the CDs
const Common::FSNode gameDataDir(ConfMan.get("path"));
@@ -868,6 +866,11 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
//_midiMusic->setNativeMT32(native_mt32);
//_midiMusic->setAdLib(adlib);
+ if (native_mt32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
+
_musicVolume = ConfMan.getInt("music_volume");
_sound = new SoundManager(this);
@@ -906,17 +909,6 @@ TinselEngine::~TinselEngine() {
MemoryDeinit();
}
-void TinselEngine::syncSoundSettings() {
- // 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");
-
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
- _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
-}
-
Common::String TinselEngine::getSavegameFilename(int16 saveNum) const {
char filename[256];
snprintf(filename, 256, "%s.%03d", getTargetName().c_str(), saveNum);
@@ -1180,7 +1172,11 @@ void TinselEngine::RestartDrivers() {
}
// Set midi volume
- SetMidiVolume(_vm->_config->_musicVolume);
+ bool mute = false;
+ if (ConfMan.hasKey("mute"))
+ mute = ConfMan.getBool("mute");
+
+ SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
}
/**
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index df27a1e0e1..ed70979349 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -74,13 +74,14 @@ enum TinselGameFeatures {
GF_FLOPPY = 1 << 2,
GF_SCNFILES = 1 << 3,
GF_ENHANCED_AUDIO_SUPPORT = 1 << 4,
+ GF_ALT_MIDI = 1 << 5, // Alternate sequence in midi.dat file
// The GF_USE_?FLAGS values specify how many country flags are displayed
// in the subtitles options dialog.
// None of these defined -> 1 language, in ENGLISH.TXT
- GF_USE_3FLAGS = 1 << 5, // French, German, Spanish
- GF_USE_4FLAGS = 1 << 6, // French, German, Spanish, Italian
- GF_USE_5FLAGS = 1 << 7 // All 5 flags
+ GF_USE_3FLAGS = 1 << 6, // French, German, Spanish
+ GF_USE_4FLAGS = 1 << 7, // French, German, Spanish, Italian
+ GF_USE_5FLAGS = 1 << 8 // All 5 flags
};
/**
@@ -169,7 +170,6 @@ protected:
#if 0
bool canSaveGameStateCurrently();
#endif
- virtual void syncSoundSettings();
public:
TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc);
diff --git a/engines/toon/anim.cpp b/engines/toon/anim.cpp
new file mode 100644
index 0000000000..157422cd9d
--- /dev/null
+++ b/engines/toon/anim.cpp
@@ -0,0 +1,703 @@
+/* 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 "toon/anim.h"
+#include "toon/toon.h"
+#include "toon/tools.h"
+
+namespace Toon {
+
+bool Animation::loadAnimation(Common::String file) {
+ debugC(1, kDebugAnim, "loadAnimation(%s)", file.c_str());
+
+ uint32 fileSize = 0;
+ uint8 *fileData = _vm->resources()->getFileData(file, &fileSize);
+ if (!fileData)
+ return false;
+
+ strcpy(_name, "not_loaded");
+ if (strncmp((char *)fileData, "KevinAguilar", 12))
+ return false;
+
+ strcpy(_name, file.c_str());
+
+ uint32 headerSize = READ_LE_UINT32(fileData + 16);
+ uint32 uncompressedBytes = READ_LE_UINT32(fileData + 20);
+ uint32 compressedBytes = READ_LE_UINT32(fileData + 24);
+ _numFrames = READ_LE_UINT32(fileData + 28);
+ _x1 = READ_LE_UINT32(fileData + 32);
+ _y1 = READ_LE_UINT32(fileData + 36);
+ _x2 = READ_LE_UINT32(fileData + 40);
+ _y2 = READ_LE_UINT32(fileData + 44);
+ _paletteEntries = READ_LE_UINT32(fileData + 56);
+ _fps = READ_LE_UINT32(fileData + 60);
+ uint32 paletteSize = READ_LE_UINT32(fileData + 64);
+
+ uint8 *currentData = fileData + 68;
+ if (_paletteEntries) {
+ if (paletteSize) {
+ _palette = new uint8[paletteSize];
+ memcpy(_palette, currentData, paletteSize);
+ currentData += paletteSize;
+ } else {
+ _palette = 0;
+ }
+ }
+
+ byte *finalBuffer = new byte[uncompressedBytes];
+ if (uncompressedBytes > compressedBytes)
+ decompressLZSS(currentData, finalBuffer, uncompressedBytes);
+ else
+ memcpy(finalBuffer, currentData, uncompressedBytes);
+
+ if (READ_LE_UINT32(finalBuffer) == 0x12345678) {
+ uint8 *data = finalBuffer;
+ _frames = new AnimationFrame[_numFrames];
+ for (int32 e = 0; e < _numFrames; e++) {
+ if (READ_LE_UINT32(data) != 0x12345678)
+ return false;
+
+ int32 oldRef = READ_LE_UINT32(data + 4);
+ uint32 compressedSize = READ_LE_UINT32(data + 8);
+ uint32 decompressedSize = READ_LE_UINT32(data + 12);
+
+ _frames[e]._x1 = READ_LE_UINT32(data + 16);
+ _frames[e]._y1 = READ_LE_UINT32(data + 20);
+ _frames[e]._x2 = READ_LE_UINT32(data + 24);
+ _frames[e]._y2 = READ_LE_UINT32(data + 28);
+
+ uint8 *imageData = data + headerSize;
+ if (oldRef != -1 || decompressedSize == 0) {
+ _frames[e]._ref = oldRef;
+ _frames[e]._data = 0;
+ } else {
+ _frames[e]._ref = -1;
+ _frames[e]._data = new uint8[decompressedSize];
+ decompressLZSS(imageData, _frames[e]._data, decompressedSize);
+ }
+
+ data += headerSize + compressedSize;
+ }
+ }
+
+ delete[] finalBuffer;
+ return true;
+}
+
+Animation::Animation(ToonEngine *vm) : _vm(vm) {
+ _palette = 0;
+ _frames = 0;
+}
+
+Animation::~Animation() {
+ delete[] _palette;
+ for (int32 i = 0; i < _numFrames; i++) {
+ delete[] _frames[i]._data;
+ }
+ delete[] _frames;
+}
+
+Common::Rect Animation::getRect() {
+ debugC(5, kDebugAnim, "getRect");
+ return Common::Rect(_x1, _y1, _x2, _y2);
+}
+
+void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy) {
+ debugC(3, kDebugAnim, "drawFrame(surface, %d, %d, %d)", frame, xx, yy);
+ if (frame < 0)
+ frame = 0;
+
+ if (frame >= _numFrames)
+ frame = _numFrames - 1;
+
+ if (_numFrames == 0)
+ return;
+
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+
+ int32 rectX = _frames[frame]._x2 - _frames[frame]._x1;
+ int32 rectY = _frames[frame]._y2 - _frames[frame]._y1;
+
+ if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
+ return;
+
+ if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w)
+ rectX = surface.w - xx - _x1 - _frames[frame]._x1;
+
+ if (rectX < 0)
+ return;
+
+ if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h)
+ rectY = surface.h - yy - _y1 - _frames[frame]._y1;
+
+ if (rectY < 0)
+ return;
+
+ int32 destPitch = surface.pitch;
+ uint8 *srcRow = _frames[frame]._data;
+ uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1);
+ for (int32 y = 0; y < rectY; y++) {
+ uint8 *cur = curRow;
+ uint8 *c = srcRow + y * (_frames[frame]._x2 - _frames[frame]._x1);
+ for (int32 x = 0; x < rectX; x++) {
+ if (*c)
+ *cur = *c;
+ c++;
+ cur++;
+ }
+ curRow += destPitch;
+ }
+}
+
+void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask) {
+ debugC(1, kDebugAnim, "drawFrameWithMask(surface, %d, %d, %d, %d, mask)", frame, xx, yy, zz);
+ warning("STUB: drawFrameWithMask()");
+}
+
+void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale) {
+ debugC(5, kDebugAnim, "drawFrameWithMaskAndScale(surface, %d, %d, %d, %d, mask, %d)", frame, xx, yy, zz, scale);
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+ int32 rectX = _frames[frame]._x2 - _frames[frame]._x1;
+ int32 rectY = _frames[frame]._y2 - _frames[frame]._y1;
+
+ int32 finalWidth = rectX * scale / 1024;
+ int32 finalHeight = rectY * scale / 1024;
+
+ // compute final x1,y1,x2,y2
+ int32 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024;
+ int32 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024;
+ int32 xx2 = xx1 + finalWidth;
+ int32 yy2 = yy1 + finalHeight;
+ int32 w = _frames[frame]._x2 - _frames[frame]._x1;
+// Strangerke - Commented (not used)
+// int32 h = _frames[frame]._y2 - _frames[frame]._y1;
+
+ int32 destPitch = surface.pitch;
+ int32 destPitchMask = mask->getWidth();
+ uint8 *c = _frames[frame]._data;
+ uint8 *curRow = (uint8 *)surface.pixels;
+ uint8 *curRowMask = mask->getDataPtr();
+
+ if (strstr(_name, "shadow")) {
+ for (int y = yy1; y < yy2; y++) {
+ for (int x = xx1; x < xx2; x++) {
+ if (x < 0 || x >= 1280 || y < 0 || y >= 400)
+ continue;
+
+ uint8 *cur = curRow + x + y * destPitch;
+ uint8 *curMask = curRowMask + x + y * destPitchMask;
+
+ // find the good c
+ int32 xs = (x - xx1) * 1024 / scale;
+ int32 ys = (y - yy1) * 1024 / scale;
+ uint8 *cc = &c[ys * w + xs];
+ if (*cc && ((*curMask) >= zz))
+ *cur = _vm->getShadowLUT()[*cur];
+ }
+ }
+ } else {
+ for (int y = yy1; y < yy2; y++) {
+ for (int x = xx1; x < xx2; x++) {
+ if (x < 0 || x >= 1280 || y < 0 || y >= 400)
+ continue;
+
+ uint8 *cur = curRow + x + y * destPitch;
+ uint8 *curMask = curRowMask + x + y * destPitchMask;
+
+ // find the good c
+ int32 xs = (x - xx1) * 1024 / scale;
+ int32 ys = (y - yy1) * 1024 / scale;
+ uint8 *cc = &c[ys * w + xs];
+ if (*cc && ((*curMask) >= zz))
+ *cur = *cc;
+ }
+ }
+ }
+}
+
+void Animation::applyPalette(int32 offset, int32 srcOffset, int32 numEntries) {
+ debugC(1, kDebugAnim, "applyPalette(%d, %d, %d)", offset, srcOffset, numEntries);
+ _vm->setPaletteEntries(_palette + srcOffset, offset, numEntries);
+}
+
+int32 Animation::getFrameWidth(int32 frame) {
+ debugC(4, kDebugAnim, "getFrameWidth(%d)", frame);
+ if ((frame < 0) || (frame >= _numFrames))
+ return 0;
+
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+
+ return _frames[frame]._x2 - _frames[frame]._x1;
+}
+
+int32 Animation::getFrameHeight(int32 frame) {
+ debugC(4, kDebugAnim, "getFrameHeight(%d)", frame);
+ if (frame < 0 || frame >= _numFrames)
+ return 0;
+
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+
+ return _frames[frame]._y2 - _frames[frame]._y1;
+}
+
+int32 Animation::getWidth() const {
+ return _x2 - _x1;
+}
+
+int32 Animation::getHeight() const {
+ return _y2 - _y1;
+}
+
+void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, byte *colorMap) {
+ debugC(4, kDebugAnim, "drawFontFrame(surface, %d, %d, %d, colorMap)", frame, xx, yy);
+ if (frame < 0)
+ frame = 0;
+
+ if (frame >= _numFrames)
+ frame = _numFrames - 1;
+
+ if (_numFrames == 0)
+ return;
+
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+
+ int32 rectX = _frames[frame]._x2 - _frames[frame]._x1;
+ int32 rectY = _frames[frame]._y2 - _frames[frame]._y1;
+
+ if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
+ return;
+
+ if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w)
+ rectX = surface.w - xx - _x1 - _frames[frame]._x1;
+
+ if (rectX < 0)
+ return;
+
+ if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h)
+ rectY = surface.h - yy - _y1 - _frames[frame]._y1;
+
+ if (rectY < 0)
+ return;
+
+ int32 destPitch = surface.pitch;
+ uint8 *c = _frames[frame]._data;
+ uint8 *curRow = (uint8 *)surface.pixels + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1);
+ for (int32 y = 0; y < rectY; y++) {
+ unsigned char *cur = curRow;
+ for (int32 x = 0; x < rectX; x++) {
+ if (*c && *c < 4)
+ *cur = colorMap[*c];
+ c++;
+ cur++;
+ }
+ curRow += destPitch;
+ }
+}
+
+void Animation::drawFrameOnPicture(int32 frame, int32 xx, int32 yy) {
+ debugC(1, kDebugAnim, "drawFrameOnPicture(%d, %d, %d)", frame, xx, yy);
+ if (frame < 0)
+ frame = 0;
+
+ if (frame >= _numFrames)
+ frame = _numFrames - 1;
+
+ if (_numFrames == 0)
+ return;
+
+ if (_frames[frame]._ref != -1)
+ frame = _frames[frame]._ref;
+
+ int32 rectX = _frames[frame]._x2 - _frames[frame]._x1;
+ int32 rectY = _frames[frame]._y2 - _frames[frame]._y1;
+
+ Picture *pic = _vm->getPicture();
+
+ if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
+ return;
+
+ if (rectX + xx + _x1 + _frames[frame]._x1 >= pic->getWidth())
+ rectX = pic->getWidth() - xx - _x1 - _frames[frame]._x1;
+
+ if (rectX < 0)
+ return;
+
+ if (rectY + yy + _y1 + _frames[frame]._y1 >= pic->getHeight())
+ rectY = pic->getHeight() - yy - _y1 - _frames[frame]._y1;
+
+ if (rectY < 0)
+ return;
+
+ int32 destPitch = pic->getWidth();
+ uint8 *c = _frames[frame]._data;
+ uint8 *curRow = (uint8 *)pic->getDataPtr() + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1);
+ for (int32 y = 0; y < rectY; y++) {
+ unsigned char *cur = curRow;
+ for (int32 x = 0; x < rectX; x++) {
+ if (*c)
+ *cur = *c;
+ c++;
+ cur++;
+ }
+ curRow += destPitch;
+ }
+}
+
+void AnimationInstance::update(int32 timeIncrement) {
+ debugC(5, kDebugAnim, "update(%d)", timeIncrement);
+ if (_currentFrame == -1)
+ return;
+
+ if (_rangeStart == _rangeEnd) {
+ _currentFrame = _rangeStart;
+ return;
+ }
+
+ if (_playing) {
+ _currentTime += timeIncrement;
+ _currentFrame = _currentTime / (1000 / _fps);
+ }
+
+ if (_looping) {
+ _currentFrame = (_currentFrame % (_rangeEnd - _rangeStart + 1)) + _rangeStart;
+ } else {
+ if (_currentFrame >= _rangeEnd - _rangeStart) {
+ _playing = false;
+ _currentFrame = _rangeEnd;
+ } else {
+ _currentFrame = _rangeStart + _currentFrame;
+ }
+ }
+}
+
+AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type) : _vm(vm) {
+ _id = 0;
+ _type = type;
+ _animation = 0;
+ _currentFrame = 0;
+ _currentTime = 0;
+ _fps = 15;
+ _looping = true;
+ _playing = false;
+ _rangeEnd = 0;
+ _useMask = false;
+ _rangeStart = 0;
+ _scale = 1024;
+ _x = 0;
+ _y = 0;
+ _z = 0;
+ _layerZ = 0;
+}
+
+void AnimationInstance::render() {
+ debugC(5, kDebugAnim, "render()");
+ if (_visible && _animation) {
+ int32 frame = _currentFrame;
+ if (frame < 0)
+ frame = 0;
+
+ if (frame >= _animation->_numFrames)
+ frame = _animation->_numFrames - 1;
+
+ if (_useMask) {
+ //if (_scale == 100) { // 100% scale
+ // _animation->drawFrameWithMask(_vm->getMainSurface(), _currentFrame, _x, _y, _z, _vm->getMask());
+ //} else {
+ _animation->drawFrameWithMaskAndScale(_vm->getMainSurface(), frame, _x, _y, _z, _vm->getMask(), _scale);
+ //}
+ } else {
+ _animation->drawFrame(_vm->getMainSurface(), frame, _x, _y);
+ }
+ }
+}
+
+void AnimationInstance::renderOnPicture() {
+ debugC(5, kDebugAnim, "renderOnPicture()");
+ if (_visible && _animation)
+ _animation->drawFrameOnPicture(_currentFrame, _x, _y);
+}
+
+void AnimationInstance::playAnimation() {
+ debugC(6, kDebugAnim, "playAnimation()");
+ _playing = true;
+}
+
+void AnimationInstance::setAnimation(Animation *animation, bool setRange) {
+ debugC(5, kDebugAnim, "setAnimation(animation)");
+ _animation = animation;
+ if (animation && setRange) {
+ _rangeStart = 0;
+ _rangeEnd = animation->_numFrames - 1;
+ }
+}
+
+void AnimationInstance::setAnimationRange(int32 rangeStart, int rangeEnd) {
+ debugC(5, kDebugAnim, "setAnimationRange(%d, %d)", rangeStart, rangeEnd);
+ _rangeStart = rangeStart;
+ _rangeEnd = rangeEnd;
+
+ if (_currentFrame < _rangeStart)
+ _currentFrame = _rangeStart;
+
+ if (_currentFrame > _rangeEnd)
+ _currentFrame = _rangeEnd;
+}
+
+void AnimationInstance::setPosition(int32 x, int32 y, int32 z, bool relative) {
+ debugC(5, kDebugAnim, "setPosition(%d, %d, %d, %d)", x, y, z, (relative) ? 1 : 0);
+ if (relative || !_animation) {
+ _x = x;
+ _y = y;
+ _z = z;
+ } else {
+ _x = x - _animation->_x1;
+ _y = y - _animation->_y1;
+ _z = z;
+ }
+}
+
+void AnimationInstance::moveRelative(int32 dx, int32 dy, int32 dz) {
+ debugC(1, kDebugAnim, "moveRelative(%d, %d, %d)", dx, dy, dz);
+ _x += dx;
+ _y += dy;
+ _z += dz;
+}
+
+void AnimationInstance::forceFrame(int32 position) {
+ debugC(5, kDebugAnim, "forceFrame(%d)", position);
+ _currentFrame = position;
+ _rangeStart = position;
+ _rangeEnd = position;
+}
+
+void AnimationInstance::setFrame(int32 position) {
+ debugC(5, kDebugAnim, "setFrame(%d)", position);
+ _currentFrame = position;
+}
+
+void AnimationInstance::setFps(int32 fps) {
+ debugC(4, kDebugAnim, "setFps(%d)", fps);
+ _fps = fps;
+}
+
+void AnimationInstance::stopAnimation() {
+ debugC(5, kDebugAnim, "stopAnimation()");
+ _playing = false;
+}
+
+void AnimationInstance::setVisible(bool visible) {
+ debugC(1, kDebugAnim, "setVisible(%d)", (visible) ? 1 : 0);
+ _visible = visible;
+}
+
+void AnimationInstance::setScale(int32 scale) {
+ debugC(4, kDebugAnim, "setScale(%d)", scale);
+ _scale = scale;
+}
+
+void AnimationInstance::setUseMask(bool useMask) {
+ debugC(1, kDebugAnim, "setUseMask(%d)", (useMask) ? 1 : 0);
+ _useMask = useMask;
+}
+
+void AnimationInstance::getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const {
+ debugC(5, kDebugAnim, "getRect(%d, %d, %d, %d)", *x1, *y1, *x2, *y2);
+ int32 rectX = _animation->_frames[_currentFrame]._x2 - _animation->_frames[_currentFrame]._x1;
+ int32 rectY = _animation->_frames[_currentFrame]._y2 - _animation->_frames[_currentFrame]._y1;
+
+ int32 finalWidth = rectX * _scale / 1024;
+ int32 finalHeight = rectY * _scale / 1024;
+
+ // compute final x1,y1,x2,y2
+ *x1 = _x + _animation->_x1 + _animation->_frames[_currentFrame]._x1 * _scale / 1024;
+ *y1 = _y + _animation->_y1 + _animation->_frames[_currentFrame]._y1 * _scale / 1024;
+ *x2 = *x1 + finalWidth;
+ *y2 = *y1 + finalHeight;
+}
+
+void AnimationInstance::setX(int32 x, bool relative) {
+ debugC(1, kDebugAnim, "setX(%d, %d)", x, (relative) ? 1 : 0);
+ if (relative || !_animation)
+ _x = x;
+ else
+ _x = x - _animation->_x1;
+}
+
+void AnimationInstance::setY(int32 y, bool relative) {
+ debugC(1, kDebugAnim, "setY(%d, %d)", y, (relative) ? 1 : 0);
+ if (relative || !_animation)
+ _y = y;
+ else
+ _y = y - _animation->_y1;
+}
+
+void AnimationInstance::setZ(int32 z, bool relative) {
+ debugC(1, kDebugAnim, "setZ(%d, %d)", z, (relative) ? 1 : 0);
+ _z = z;
+}
+
+void AnimationInstance::setLayerZ(int32 z) {
+ _layerZ = z;
+}
+
+int32 AnimationInstance::getLayerZ() const {
+ return _layerZ;
+}
+
+int32 AnimationInstance::getX2() const {
+ return _x + _animation->_x1;
+}
+
+int32 AnimationInstance::getY2() const {
+ return _y + _animation->_y1;
+}
+
+int32 AnimationInstance::getZ2() const {
+ return _z;
+}
+
+void AnimationInstance::save(Common::WriteStream *stream) {
+ // we don't load the animation here
+ // it must be loaded externally to avoid leaks.
+ stream->writeSint32LE(_currentFrame);
+ stream->writeSint32LE(_currentTime);
+ stream->writeSint32LE(_layerZ);
+ stream->writeSint32LE(_x);
+ stream->writeSint32LE(_y);
+ stream->writeSint32LE(_z);
+ stream->writeSint32LE(_scale);
+ stream->writeSint32LE(_playing);
+ stream->writeSint32LE(_looping);
+ stream->writeSint32LE(_rangeStart);
+ stream->writeSint32LE(_rangeEnd);
+ stream->writeSint32LE(_rangeStart);
+ stream->writeSint32LE(_fps);
+ stream->writeSint32LE(_id);
+ stream->writeSint32LE(_type);
+ stream->writeSint32LE(_visible);
+ stream->writeSint32LE(_useMask);
+}
+void AnimationInstance::load(Common::ReadStream *stream) {
+ _currentFrame = stream->readSint32LE();
+ _currentTime = stream->readSint32LE();
+ _layerZ = stream->readSint32LE();
+ _x = stream->readSint32LE();
+ _y = stream->readSint32LE();
+ _z = stream->readSint32LE();
+ _scale = stream->readSint32LE();
+ _playing = stream->readSint32LE();
+ _looping = stream->readSint32LE();
+ _rangeStart = stream->readSint32LE();
+ _rangeEnd = stream->readSint32LE();
+ _rangeStart = stream->readSint32LE();
+ _fps = stream->readSint32LE();
+ _id = stream->readSint32LE();
+ _type = (AnimationInstanceType)stream->readSint32LE();
+ _visible = stream->readSint32LE();
+ _useMask = stream->readSint32LE();
+}
+
+
+
+void AnimationInstance::setLooping(bool enable) {
+ debugC(6, kDebugAnim, "setLooping(%d)", (enable) ? 1 : 0);
+ _looping = enable;
+}
+
+void AnimationInstance::reset() {
+ _currentFrame = 0;
+ _currentTime = 0;
+}
+
+AnimationManager::AnimationManager(ToonEngine *vm) : _vm(vm) {
+}
+
+void AnimationManager::addInstance(AnimationInstance *instance) {
+ _instances.push_back(instance);
+}
+
+void AnimationManager::removeInstance(AnimationInstance *instance) {
+ debugC(1, kDebugAnim, "removeInstance(instance)");
+ int32 found = -1;
+ for (uint32 i = 0; i < _instances.size(); i++) {
+ if (_instances[i] == instance) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found > -1)
+ _instances.remove_at(found);
+}
+
+void AnimationManager::removeAllInstances(AnimationInstanceType type) {
+ debugC(1, kDebugAnim, "removeInstance(type)");
+ for (int32 i = (int32)_instances.size(); i >= 0; i--) {
+ if (_instances[i]->getType() & type)
+ _instances.remove_at(i);
+ }
+}
+
+void AnimationManager::update(int32 timeIncrement) {
+ debugC(5, kDebugAnim, "update(%d)", timeIncrement);
+ for (uint32 i = 0; i < _instances.size(); i++)
+ _instances[i]->update(timeIncrement);
+}
+
+void AnimationManager::render() {
+ debugC(5, kDebugAnim, "render()");
+ // sort the instance by layer z
+ // bubble sort (replace by faster afterwards)
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ for (uint32 i = 0; i < _instances.size() - 1; i++) {
+ if ((_instances[i]->getLayerZ() > _instances[i + 1]->getLayerZ()) ||
+ ((_instances[i]->getLayerZ() == _instances[i + 1]->getLayerZ()) &&
+ (_instances[i]->getId() < _instances[i+1]->getId()))) {
+ AnimationInstance *instance = _instances[i];
+ _instances[i] = _instances[i + 1];
+ _instances[i + 1] = instance;
+ changed = true;
+ }
+ }
+ }
+
+ for (uint32 i = 0; i < _instances.size(); i++) {
+ if (_instances[i]->getVisible())
+ _instances[i]->render();
+ }
+}
+
+AnimationInstance *AnimationManager::createNewInstance(AnimationInstanceType type) {
+ return new AnimationInstance(_vm, type);
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/anim.h b/engines/toon/anim.h
new file mode 100644
index 0000000000..7bf633220c
--- /dev/null
+++ b/engines/toon/anim.h
@@ -0,0 +1,194 @@
+/* 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 TOON_ANIM_H
+#define TOON_ANIM_H
+
+#include "common/stream.h"
+#include "common/array.h"
+#include "common/func.h"
+#include "graphics/surface.h"
+
+#include "toon/script.h"
+
+namespace Toon {
+
+class Picture;
+class ToonEngine;
+
+struct AnimationFrame {
+ int32 _x1;
+ int32 _y1;
+ int32 _x2;
+ int32 _y2;
+ int32 _ref;
+ uint8 *_data;
+};
+
+class Animation {
+public:
+ Animation(ToonEngine *vm);
+ ~Animation();
+
+ int32 _x1;
+ int32 _y1;
+ int32 _x2;
+ int32 _y2;
+ int32 _numFrames;
+ int32 _fps;
+ AnimationFrame *_frames;
+ uint8 *_palette;
+ int32 _paletteEntries;
+ char _name[32];
+
+ bool loadAnimation(Common::String file);
+ void drawFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y);
+ void drawFontFrame(Graphics::Surface &surface, int32 frame, int32 x, int32 y, byte *colorMap);
+ void drawFrameOnPicture(int32 frame, int32 x, int32 y);
+ void drawFrameWithMask(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask);
+ void drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int32 xx, int32 yy, int32 zz, Picture *mask, int32 scale);
+ void drawStrip(int32 offset = 0);
+ void applyPalette(int32 offset, int32 srcOffset, int32 numEntries);
+ int32 getFrameWidth(int32 frame);
+ int32 getFrameHeight(int32 frame);
+ int32 getWidth() const;
+ int32 getHeight() const;
+ Common::Rect getRect();
+protected:
+ ToonEngine *_vm;
+};
+
+enum AnimationInstanceType {
+ kAnimationCharacter = 1,
+ kAnimationScene = 2,
+ kAnimationCursor = 4
+};
+
+class AnimationInstance {
+public:
+ AnimationInstance(ToonEngine *vm, AnimationInstanceType type);
+ void update(int32 timeIncrement);
+ void render();
+ void renderOnPicture();
+ void setAnimation(Animation *animation, bool setRange = true);
+ void playAnimation();
+ void setAnimationRange(int32 rangeStart, int rangeEnd);
+ void setFps(int32 fps);
+ void setLooping(bool enable);
+ void stopAnimation();
+ void setFrame(int32 position);
+ void forceFrame(int32 position);
+ void setPosition(int32 x, int32 y, int32 z, bool relative = false);
+ Animation *getAnimation() const { return _animation; }
+ void setScale(int32 scale);
+ void setVisible(bool visible);
+ bool getVisible() const { return _visible; }
+ void setUseMask(bool useMask);
+ void moveRelative(int32 dx, int32 dy, int32 dz);
+ void getRect(int32 *x1, int32 *y1, int32 *x2, int32 *y2) const;
+ int32 getX() const { return _x; }
+ int32 getY() const { return _y; }
+ int32 getZ() const { return _z; }
+ int32 getX2() const;
+ int32 getY2() const;
+ int32 getZ2() const;
+ int32 getFrame() const { return _currentFrame; }
+ void reset();
+ void save(Common::WriteStream *stream);
+ void load(Common::ReadStream *stream);
+
+ void setId(int32 id) { _id = id; }
+ int32 getId() const { return _id; }
+
+ void setX(int32 x, bool relative = false);
+ void setY(int32 y, bool relative = false);
+ void setZ(int32 z, bool relative = false);
+ void setLayerZ(int32 layer);
+ int32 getLayerZ() const;
+
+ AnimationInstanceType getType() const { return _type; }
+
+protected:
+ int32 _currentFrame;
+ int32 _currentTime;
+ int32 _fps;
+ Animation *_animation;
+ int32 _x;
+ int32 _y;
+ int32 _z;
+ int32 _layerZ;
+ int32 _rangeStart;
+ int32 _rangeEnd;
+ int32 _scale;
+ int32 _id;
+
+ AnimationInstanceType _type;
+
+ bool _useMask;
+ bool _playing;
+ bool _looping;
+ bool _visible;
+
+ ToonEngine *_vm;
+};
+
+class AnimationManager {
+public:
+ AnimationManager(ToonEngine *vm);
+ AnimationInstance *createNewInstance(AnimationInstanceType type);
+ void addInstance(AnimationInstance *instance);
+ void removeInstance(AnimationInstance *instance);
+ void removeAllInstances(AnimationInstanceType type);
+ void render();
+ void update(int32 timeIncrement);
+
+protected:
+ ToonEngine *_vm;
+ Common::Array<AnimationInstance *> _instances;
+};
+
+class SceneAnimation {
+public:
+ AnimationInstance *_animInstance;
+ Animation *_animation;
+ int32 _id;
+ bool _active;
+
+ void load(ToonEngine *vm, Common::ReadStream *stream);
+ void save(ToonEngine *vm, Common::WriteStream *stream);
+};
+
+class SceneAnimationScript {
+public:
+ EMCData *_data;
+ EMCState _state;
+ uint32 _lastTimer;
+ bool _frozen;
+ bool _active;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/audio.cpp b/engines/toon/audio.cpp
new file mode 100644
index 0000000000..496d626201
--- /dev/null
+++ b/engines/toon/audio.cpp
@@ -0,0 +1,480 @@
+/* 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 "toon/audio.h"
+
+namespace Toon {
+
+static int ADPCM_index[8] = {
+ -1, -1, -1, -1, 2 , 4 , 6 , 8
+};
+static int ADPCM_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+};
+
+AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+ for (int32 i = 0; i < 16; i++)
+ _channels[i] = 0;
+}
+
+AudioManager::~AudioManager(void) {
+}
+
+void AudioManager::removeInstance(AudioStreamInstance *inst) {
+ debugC(1, kDebugAudio, "removeInstance(inst)");
+
+ for (int32 i = 0; i < 16; i++) {
+ if (inst == _channels[i])
+ _channels[i] = 0;
+ }
+}
+
+void AudioManager::playMusic(Common::String dir, Common::String music) {
+ debugC(1, kDebugAudio, "playMusic(%s, %s)", dir.c_str(), music.c_str());
+
+ // two musics can be played at same time
+ Common::String path = Common::String::printf("act%d/%s/%s.mus", _vm->state()->_currentChapter, dir.c_str(), music.c_str());
+
+ if (_currentMusicName == music)
+ return;
+
+ _currentMusicName = music;
+
+ Common::SeekableReadStream *srs = _vm->resources()->openFile(path);
+ if (!srs)
+ return;
+
+ // see what channel to take
+ if (_channels[0] && _channels[0]->isPlaying() && _channels[1] && _channels[1]->isPlaying()) {
+ // take the one that is fading
+ if (_channels[0]->isFading()) {
+ _channels[0]->stop(false);
+ _channels[1]->stop(true);
+ _currentMusicChannel = 0;
+ } else {
+ _channels[1]->stop(false);
+ _channels[0]->stop(true);
+ _currentMusicChannel = 1;
+ }
+ } else if (_channels[0] && _channels[0]->isPlaying()) {
+ _channels[0]->stop(true);
+ _currentMusicChannel = 1;
+ } else {
+ if (_channels[1] && _channels[1]->isPlaying())
+ _channels[1]->stop(true);
+ _currentMusicChannel = 0;
+ }
+
+
+ //if (!_channels[_currentMusicChannel])
+ // delete _channels[_currentMusicChannel];
+ _channels[_currentMusicChannel] = new AudioStreamInstance(this, _mixer, srs, true);
+ _channels[_currentMusicChannel]->play(true, Audio::Mixer::kMusicSoundType);
+}
+
+bool AudioManager::voiceStillPlaying() {
+ if (!_channels[2])
+ return false;
+
+ return _channels[2]->isPlaying();
+}
+
+void AudioManager::playVoice(int32 id, bool genericVoice) {
+ debugC(1, kDebugAudio, "playVoice(%d, %d)", id, (genericVoice) ? 1 : 0);
+
+ if (voiceStillPlaying()) {
+ // stop current voice
+ _channels[2]->stop(false);
+ }
+
+ Common::SeekableReadStream *stream;
+ if (genericVoice)
+ stream = _audioPacks[0]->getStream(id);
+ else
+ stream = _audioPacks[1]->getStream(id);
+
+ _channels[2] = new AudioStreamInstance(this, _mixer, stream);
+ _channels[2]->play(false, Audio::Mixer::kSpeechSoundType);
+
+}
+
+void AudioManager::playSFX(int32 id, int32 volume , bool genericSFX) {
+ debugC(4, kDebugAudio, "playSFX(%d, %d)", id, (genericSFX) ? 1 : 0);
+
+ // find a free SFX channel
+ Common::SeekableReadStream *stream;
+
+ if (genericSFX)
+ stream = _audioPacks[2]->getStream(id, true);
+ else
+ stream = _audioPacks[3]->getStream(id, true);
+
+ if (stream->size() == 0)
+ return;
+
+ for (int32 i = 3; i < 16; i++) {
+ if (!_channels[i]) {
+ _channels[i] = new AudioStreamInstance(this, _mixer, stream);
+ _channels[i]->play(false, Audio::Mixer::kSFXSoundType);
+ _channels[i]->setVolume(volume);
+ break;
+ }
+ }
+}
+
+void AudioManager::stopCurrentVoice() {
+ debugC(1, kDebugAudio, "stopCurrentVoice()");
+
+ if (_channels[2] && _channels[2]->isPlaying())
+ _channels[2]->stop(false);
+}
+
+bool AudioManager::loadAudioPack(int32 id, Common::String indexFile, Common::String packFile) {
+ debugC(4, kDebugAudio, "loadAudioPack(%d, %s, %s)", id, indexFile.c_str(), packFile.c_str());
+
+ _audioPacks[id] = new AudioStreamPackage(_vm);
+ return _audioPacks[id]->loadAudioPackage(indexFile, packFile);
+}
+
+void AudioManager::setMusicVolume(int32 volume) {
+ debugC(1, kDebugAudio, "setMusicVolume(%d)", volume);
+ if (_channels[0])
+ _channels[0]->setVolume(volume);
+
+ if (_channels[1])
+ _channels[1]->setVolume(volume);
+}
+
+void AudioManager::stopMusic() {
+ debugC(1, kDebugAudio, "stopMusic()");
+
+ if (_channels[0])
+ _channels[0]->stop(true);
+ if (_channels[1])
+ _channels[1]->stop(true);
+}
+AudioStreamInstance::~AudioStreamInstance() {
+ if (_man)
+ _man->removeInstance(this);
+}
+
+AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream , bool looping) {
+ _compBufferSize = 0;
+ _buffer = 0;
+ _bufferMaxSize = 0;
+ _mixer = mixer;
+ _compBuffer = 0;
+ _bufferOffset = 0;
+ _lastADPCMval1 = 0;
+ _lastADPCMval2 = 0;
+ _file = stream;
+ _fadingIn = false;
+ _fadingOut = false;
+ _fadeTime = 0;
+ _stopped = false;
+ _volume = 255;
+ _totalSize = stream->size();
+ _currentReadSize = 8;
+ _man = man;
+ _looping = looping;
+ _musicAttenuation = 1000;
+
+ // preload one packet
+ if (_totalSize > 0) {
+ _file->skip(8);
+ readPacket();
+ } else {
+ stopNow();
+ }
+}
+
+int32 AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) {
+ debugC(5, kDebugAudio, "readBuffer(buffer, %d)", numSamples);
+
+ handleFade(numSamples);
+ int32 leftSamples = numSamples;
+ int32 destOffset = 0;
+ if ((_bufferOffset + leftSamples) * 2 >= _bufferSize) {
+ if (_bufferSize - _bufferOffset * 2 > 0) {
+ memcpy(buffer, &_buffer[_bufferOffset], _bufferSize - _bufferOffset * 2);
+ leftSamples -= (_bufferSize - _bufferOffset * 2) / 2;
+ destOffset += (_bufferSize - _bufferOffset * 2) / 2;
+ }
+ if (!readPacket())
+ return 0;
+
+ _bufferOffset = 0;
+ }
+
+ if (leftSamples >= 0) {
+ memcpy(buffer + destOffset, &_buffer[_bufferOffset], MIN(leftSamples * 2, _bufferSize));
+ _bufferOffset += leftSamples;
+ }
+
+ return numSamples;
+}
+
+bool AudioStreamInstance::readPacket() {
+ debugC(5, kDebugAudio, "readPacket()");
+
+ if (_file->eos() || (_currentReadSize >= _totalSize)) {
+ if (_looping) {
+ _file->seek(8);
+ _currentReadSize = 8;
+ _lastADPCMval1 = 0;
+ _lastADPCMval2 = 0;
+ } else {
+ _bufferSize = 0;
+ stopNow();
+ return false;
+ }
+ }
+ int16 numCompressedBytes = _file->readSint16LE();
+ int16 numDecompressedBytes = _file->readSint16LE();
+ _file->readSint32LE();
+
+ if (numCompressedBytes > _compBufferSize) {
+ if (_compBuffer)
+ delete[] _compBuffer;
+ _compBufferSize = numCompressedBytes;
+ _compBuffer = new uint8[_compBufferSize];
+ }
+
+ if (numDecompressedBytes > _bufferMaxSize) {
+ if (_buffer)
+ delete [] _buffer;
+ _bufferMaxSize = numDecompressedBytes;
+ _buffer = new int16[numDecompressedBytes];
+ }
+
+ _bufferSize = numDecompressedBytes;
+ _file->read(_compBuffer, numCompressedBytes);
+ _currentReadSize += 8 + numCompressedBytes;
+
+ decodeADPCM(_compBuffer, _buffer, numCompressedBytes);
+ return true;
+}
+
+void AudioStreamInstance::decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize) {
+ debugC(5, kDebugAudio, "decodeADPCM(comp, dest, %d)", packetSize);
+
+ int32 numSamples = 2 * packetSize;
+ int32 v18 = _lastADPCMval1;
+ int32 v19 = _lastADPCMval2;
+ for (int32 i = 0; i < numSamples; i++) {
+ uint8 comm = *comp;
+
+ int32 v29 = i & 1;
+ int32 v30;
+ if (v29 == 0)
+ v30 = comm & 0xf;
+ else
+ v30 = (comm & 0xf0) >> 4;
+
+ int32 v31 = v30 & 0x8;
+ int32 v32 = v30 & 0x7;
+ int32 v33 = ADPCM_table[v19];
+ int32 v34 = v33 >> 3;
+ if (v32 & 4)
+ v34 += v33;
+
+ if (v32 & 2)
+ v34 += v33 >> 1;
+
+ if (v32 & 1)
+ v34 += v33 >> 2;
+
+ v19 += ADPCM_index[v32];
+ if (v19 < 0)
+ v19 = 0;
+
+ if (v19 > 88)
+ v19 = 88;
+
+ if (v31)
+ v18 -= v34;
+ else
+ v18 += v34;
+
+ if (v18 > 32767)
+ v18 = 32767;
+ else if (v18 < -32768)
+ v18 = -32768;
+
+ *dest = v18;
+ comp += v29;
+ dest++;
+ }
+
+ _lastADPCMval1 = v18;
+ _lastADPCMval2 = v19;
+}
+
+void AudioStreamInstance::play(bool fade, Audio::Mixer::SoundType soundType) {
+ debugC(1, kDebugAudio, "play(%d)", (fade) ? 1 : 0);
+
+ Audio::SoundHandle soundHandle;
+ _stopped = false;
+ _fadingIn = fade;
+ _fadeTime = 0;
+ _soundType = soundType;
+ _musicAttenuation = 1000; // max volume
+ _mixer->playStream(soundType, &_handle, this);
+ handleFade(0);
+}
+
+void AudioStreamInstance::handleFade(int numSamples) {
+ debugC(5, kDebugAudio, "handleFade(%d)", numSamples);
+
+ // Fading enabled only for music
+ if (_soundType != Audio::Mixer::kMusicSoundType)
+ return;
+
+ int32 finalVolume = _volume;
+
+ if (_fadingOut) {
+ _fadeTime += numSamples;
+
+ if (_fadeTime > 40960) {
+ _fadeTime = 40960;
+ stopNow();
+ _fadingOut = false;
+ }
+ finalVolume = _volume - _fadeTime * _volume / 40960;
+ } else {
+ if (_fadingIn) {
+ _fadeTime += numSamples;
+ if (_fadeTime > 40960) {
+ _fadeTime = 40960;
+ _fadingIn = false;
+ }
+
+ finalVolume = _volume * _fadeTime / 40960;
+ }
+ }
+
+ // the music is too loud when someone is talking
+ // smoothing to avoid big volume changes
+ if (_man->voiceStillPlaying()) {
+ _musicAttenuation -= numSamples >> 4;
+ if (_musicAttenuation < 250)
+ _musicAttenuation = 250;
+ } else {
+ _musicAttenuation += numSamples >> 5;
+ if (_musicAttenuation > 1000)
+ _musicAttenuation = 1000;
+ }
+
+
+ _mixer->setChannelVolume(_handle, finalVolume * _musicAttenuation / 1000);
+
+}
+
+void AudioStreamInstance::stop(bool fade /*= false*/) {
+ debugC(1, kDebugAudio, "stop(%d)", (fade) ? 1 : 0);
+
+ if (fade) {
+ _fadingIn = false;
+ _fadingOut = true;
+ _fadeTime = 0;
+ } else {
+ stopNow();
+ }
+}
+
+void AudioStreamInstance::stopNow() {
+ debugC(1, kDebugAudio, "stopNow()");
+
+ _stopped = true;
+}
+
+void AudioStreamInstance::setVolume(int32 volume) {
+ debugC(1, kDebugAudio, "setVolume(%d)", volume);
+
+ _volume = volume;
+ _mixer->setChannelVolume(_handle, volume);
+}
+
+AudioStreamPackage::AudioStreamPackage(ToonEngine *vm) : _vm(vm) {
+ _indexBuffer = 0;
+}
+
+AudioStreamPackage::~AudioStreamPackage() {
+ delete[] _indexBuffer;
+}
+
+bool AudioStreamPackage::loadAudioPackage(Common::String indexFile, Common::String streamFile) {
+ debugC(4, kDebugAudio, "loadAudioPackage(%s, %s)", indexFile.c_str(), streamFile.c_str());
+
+ uint32 size = 0;
+ uint8 *fileData = _vm->resources()->getFileData(indexFile, &size);
+ if (!fileData)
+ return false;
+
+ delete[] _indexBuffer;
+
+ _indexBuffer = new uint32[size / 4];
+ memcpy(_indexBuffer, fileData, size);
+
+ _file = _vm->resources()->openFile(streamFile);
+ if (!_file)
+ return false;
+
+ return true;
+}
+
+void AudioStreamPackage::getInfo(int32 id, int32 *offset, int32 *size) {
+ debugC(1, kDebugAudio, "getInfo(%d, offset, size)", id);
+
+ *offset = READ_LE_UINT32(_indexBuffer + id);
+ *size = READ_LE_UINT32(_indexBuffer + id + 1) - READ_LE_UINT32(_indexBuffer + id);
+}
+
+Common::SeekableReadStream *AudioStreamPackage::getStream(int32 id, bool ownMemory) {
+ debugC(1, kDebugAudio, "getStream(%d, %d)", id, (ownMemory) ? 1 : 0);
+
+ int32 offset = 0;
+ int32 size = 0;
+ getInfo(id, &offset, &size);
+ if (ownMemory) {
+ byte *memory = new byte[size];
+ _file->seek(offset);
+ _file->read(memory, size);
+ return new Common::MemoryReadStream(memory, size, DisposeAfterUse::YES);
+ } else {
+ return new Common::SeekableSubReadStream(_file, offset, size + offset);
+ }
+}
+
+} // End of namespace Toon
+
diff --git a/engines/toon/audio.h b/engines/toon/audio.h
new file mode 100644
index 0000000000..5a8274e086
--- /dev/null
+++ b/engines/toon/audio.h
@@ -0,0 +1,147 @@
+/* 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 TOON_AUDIO_H
+#define TOON_AUDIO_H
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+#include "toon/toon.h"
+
+namespace Toon {
+
+// used for music/voice/everything
+class AudioManager;
+class AudioStreamInstance : public Audio::AudioStream {
+
+public:
+ AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream, bool looping = false);
+ ~AudioStreamInstance();
+ void play(bool fade = false, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
+ void stop(bool fade = false);
+
+ bool isPlaying() {
+ return !_stopped;
+ }
+ bool isLooping() {
+ return _looping;
+ }
+ bool isFading() {
+ return _fadingIn || _fadingOut;
+ }
+
+ void setVolume(int32 volume);
+protected:
+ int32 readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const {
+ return false;
+ }
+ int getRate() const {
+ return 22100;
+ }
+ bool endOfData() const {
+ return _stopped;
+ }
+ void handleFade(int32 numSamples);
+ void stopNow();
+
+ bool readPacket();
+ void decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize);
+
+ Common::SeekableReadStream *_file;
+ bool _fadingIn;
+ bool _fadingOut;
+ int32 _fadeTime;
+ uint8 *_compBuffer;
+ int16 *_buffer;
+ int32 _bufferSize;
+ int32 _bufferMaxSize;
+ int32 _bufferOffset;
+ int32 _compBufferSize;
+ Audio::SoundHandle _handle;
+ Audio::Mixer::SoundType _soundType;
+ Audio::Mixer *_mixer;
+ int32 _lastADPCMval1;
+ int32 _lastADPCMval2;
+ bool _stopped;
+ AudioManager *_man;
+ int32 _totalSize;
+ int32 _currentReadSize;
+ bool _looping;
+ int32 _volume;
+ int32 _musicAttenuation;
+};
+
+class AudioStreamPackage {
+
+public:
+ AudioStreamPackage(ToonEngine *vm);
+ ~AudioStreamPackage();
+
+ bool loadAudioPackage(Common::String indexFile, Common::String streamFile);
+ void getInfo(int32 id, int32 *offset, int32 *size);
+ Common::SeekableReadStream *getStream(int32 id, bool ownMemory = false);
+protected:
+ Common::SeekableReadStream *_file;
+ uint32 *_indexBuffer;
+ ToonEngine *_vm;
+};
+
+class AudioManager {
+public:
+ void removeInstance(AudioStreamInstance *inst); // called by destructor
+
+ AudioManager(ToonEngine *vm, Audio::Mixer *mixer);
+ ~AudioManager(void);
+
+ bool voiceStillPlaying();
+
+ void playMusic(Common::String dir, Common::String music);
+ void playVoice(int32 id, bool genericVoice);
+ void playSFX(int32 id, int volume, bool genericSFX);
+ void stopCurrentVoice();
+ void setMusicVolume(int32 volume);
+ void stopMusic();
+
+
+ bool loadAudioPack(int32 id, Common::String indexFile, Common::String packFile);
+
+ AudioStreamInstance *_channels[16]; // 0-1 : music
+ // 2 : voice
+ // 3-16 : SFX
+
+ AudioStreamPackage *_audioPacks[4]; // 0 : generic streams
+ // 1 : local streams
+ // 2 : generic SFX
+ // 3 : local SFX
+ uint32 _currentMusicChannel;
+ Common::String _currentMusicName;
+ ToonEngine *_vm;
+ Audio::Mixer *_mixer;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/character.cpp b/engines/toon/character.cpp
new file mode 100644
index 0000000000..50913f89c7
--- /dev/null
+++ b/engines/toon/character.cpp
@@ -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$
+*
+*/
+
+#include "toon/character.h"
+#include "toon/drew.h"
+#include "toon/flux.h"
+#include "toon/path.h"
+
+namespace Toon {
+
+Character::Character(ToonEngine *vm) : _vm(vm) {
+ _animationInstance = 0;
+ _shadowAnimationInstance = 0;
+ _shadowAnim = 0;
+ _x = 0;
+ _y = 0;
+ _z = 0;
+ _finalX = 0;
+ _finalY = 0;
+ _specialAnim = 0;
+ _sceneAnimationId = -1;
+ _idleAnim = 0;
+ _walkAnim = 0;
+ _talkAnim = 0;
+ _facing = 0;
+ _flags = 0;
+ _animFlags = 0;
+ _id = 0;
+ _scale = 1024;
+ _blockingWalk = false;
+ _animScriptId = -1;
+ _animSpecialId = -1;
+ _animSpecialDefaultId = 0;
+ _currentPathNodeCount = 0;
+ _currentPathNode = 0;
+ _visible = true;
+ _speed = 150; // 150 = nominal drew speed
+ _lastWalkTime = 0;
+ _numPixelToWalk = 0;
+ _nextIdleTime = _vm->getSystem()->getMillis() + (_vm->randRange(0, 600) + 300) * _vm->getTickLength();
+}
+
+Character::~Character(void) {
+}
+
+void Character::init() {
+
+}
+
+void Character::setFacing(int32 facing) {
+ debugC(4, kDebugCharacter, "setFacing(%d)", facing);
+ _facing = facing;
+}
+
+void Character::setPosition(int32 x, int32 y) {
+ debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
+
+ _x = x;
+ _y = y;
+ if (_animationInstance)
+ _animationInstance->setPosition(_x, _y, _z);
+ return;
+}
+
+bool Character::walkTo(int32 newPosX, int32 newPosY) {
+ debugC(1, kDebugCharacter, "walkTo(%d, %d)", newPosX, newPosY);
+
+ if (!_visible)
+ return true;
+
+ if (_x == newPosX && _y == newPosY)
+ return true;
+
+ _vm->getPathFinding()->resetBlockingRects();
+
+ if (_id == 1) {
+ int32 sizeX = MAX(5, 40 * _vm->getDrew()->getScale() / 1024);
+ int32 sizeY = MAX(2, 20 * _vm->getDrew()->getScale() / 1024);
+ _vm->getPathFinding()->addBlockingEllipse(_vm->getDrew()->getFinalX(), _vm->getDrew()->getFinalY(), sizeX, sizeY);
+ }
+
+ _vm->getPathFinding()->findClosestWalkingPoint(newPosX, newPosY, &_finalX, &_finalY, _x, _y);
+ if (_x == _finalX && _y == _finalY)
+ return true;
+
+
+ if (_vm->getPathFinding()->findPath(_x, _y, _finalX, _finalY)) {
+
+ int32 localFinalX = _finalX;
+ int32 localFinalY = _finalY;
+
+ for (int32 a = 0; a < _vm->getPathFinding()->getPathNodeCount(); a++) {
+ _currentPathX[a] = _vm->getPathFinding()->getPathNodeX(a);
+ _currentPathY[a] = _vm->getPathFinding()->getPathNodeY(a);
+ }
+ _currentPathNodeCount = _vm->getPathFinding()->getPathNodeCount();
+ _currentPathNode = 0;
+ stopSpecialAnim();
+ _flags |= 0x1;
+ _lastWalkTime = _vm->getSystem()->getMillis();
+
+ _numPixelToWalk = 0;
+
+ if (_blockingWalk) {
+ while ((_x != newPosX || _y != newPosY) && _currentPathNode < _currentPathNodeCount && !_vm->shouldQuitGame()) {
+ if (_currentPathNode < _currentPathNodeCount - 10) {
+ int32 delta = MIN(10, _currentPathNodeCount - _currentPathNode);
+ int32 dx = _currentPathX[_currentPathNode+delta] - _x;
+ int32 dy = _currentPathY[_currentPathNode+delta] - _y;
+ setFacing(getFacingFromDirection(dx, dy));
+ playWalkAnim(0, 0);
+ }
+
+ // in 1/1000 pixels
+ _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024;
+ _lastWalkTime = _vm->getSystem()->getMillis();
+
+ while (_numPixelToWalk >= 1000 && _currentPathNode < _currentPathNodeCount) {
+ _x = _currentPathX[_currentPathNode];
+ _y = _currentPathY[_currentPathNode];
+ _currentPathNode += 1;
+ _numPixelToWalk -= 1000;
+ }
+ setPosition(_x, _y);
+
+ _vm->doFrame();
+ }
+ playStandingAnim();
+ _flags &= ~0x1;
+ _currentPathNode = 0;
+ _currentPathNodeCount = 0;
+
+ if (_x != localFinalX || _y != localFinalY) {
+ return false;
+ }
+ }
+ }
+
+ //_vm->getPathFinding()->findClosestWalkingPoint(newPosX, newPosY, &_x, &_y, _x, _y);
+ //setPosition(_x,_y);
+ return true;
+}
+
+void Character::setFlag(int flag) {
+ _flags = flag;
+}
+
+int32 Character::getFlag() {
+ return _flags;
+}
+
+int32 Character::getX() {
+ return _x;
+}
+int32 Character::getY() {
+ return _y;
+}
+
+bool Character::getVisible() {
+ return _visible;
+}
+
+void Character::setVisible(bool visible) {
+ debugC(1, kDebugCharacter, "setVisible(%d)", (visible) ? 1 : 0);
+
+ _visible = visible;
+ if (_animationInstance)
+ _animationInstance->setVisible(visible);
+
+ if (_shadowAnimationInstance)
+ _shadowAnimationInstance->setVisible(visible);
+
+ return;
+}
+
+int32 Character::getFacing() {
+ return _facing;
+}
+
+bool Character::loadWalkAnimation(Common::String animName) {
+ debugC(1, kDebugCharacter, "loadWalkAnimation(%s)", animName.c_str());
+ if (_walkAnim)
+ delete _walkAnim;
+
+ _walkAnim = new Animation(_vm);
+ return _walkAnim->loadAnimation(animName);
+}
+
+bool Character::loadIdleAnimation(Common::String animName) {
+ debugC(1, kDebugCharacter, "loadIdleAnimation(%s)", animName.c_str());
+ if (_idleAnim)
+ delete _idleAnim;
+
+ _idleAnim = new Animation(_vm);
+ return _idleAnim->loadAnimation(animName);
+}
+
+bool Character::loadTalkAnimation(Common::String animName) {
+ debugC(1, kDebugCharacter, "loadTalkAnimation(%s)", animName.c_str());
+ if (_talkAnim)
+ delete _talkAnim;
+
+ _talkAnim = new Animation(_vm);
+ return _talkAnim->loadAnimation(animName);
+}
+
+bool Character::setupPalette() {
+ return false; // only for Drew
+}
+
+void Character::playStandingAnim() {
+
+}
+
+void Character::stopSpecialAnim() {
+ debugC(4, kDebugCharacter, "stopSpecialAnim()");
+// Strangerke - Commented (not used)
+#if 0
+ if (_animSpecialId != _animSpecialDefaultId)
+ delete anim
+#endif
+ if (_animScriptId != -1)
+ _vm->getSceneAnimationScript(_animScriptId)->_frozen = false;
+
+ if (_sceneAnimationId != -1)
+ _animationInstance->setAnimation(_vm->getSceneAnimation(_sceneAnimationId)->_animation);
+
+ bool needStandingAnim = (_animFlags & 0x40) != 0;
+
+ _animSpecialId = -1;
+ _time = 0;
+ _animFlags = 0;
+ _flags &= ~1;
+ _flags &= ~4;
+
+ if (needStandingAnim) {
+ playStandingAnim();
+ }
+}
+
+void Character::update(int32 timeIncrement) {
+ debugC(5, kDebugCharacter, "update(%d)", timeIncrement);
+ if ((_flags & 0x1) && _currentPathNodeCount > 0) {
+ if (_currentPathNode < _currentPathNodeCount) {
+ if (_currentPathNode < _currentPathNodeCount - 10) {
+ int32 delta = MIN(10, _currentPathNodeCount - _currentPathNode);
+ int32 dx = _currentPathX[_currentPathNode+delta] - _x;
+ int32 dy = _currentPathY[_currentPathNode+delta] - _y;
+ setFacing(getFacingFromDirection(dx, dy));
+ playWalkAnim(0, 0);
+ }
+
+ // in 1/1000 pixels
+ _numPixelToWalk += _speed * (_vm->getSystem()->getMillis() - _lastWalkTime) * _scale / 1024;
+ _lastWalkTime = _vm->getSystem()->getMillis();
+
+ while (_numPixelToWalk > 1000 && _currentPathNode < _currentPathNodeCount) {
+ _x = _currentPathX[_currentPathNode];
+ _y = _currentPathY[_currentPathNode];
+ _currentPathNode += 1;
+ _numPixelToWalk -= 1000;
+ }
+ setPosition(_x, _y);
+ } else {
+ playStandingAnim();
+ _flags &= ~0x1;
+ _currentPathNodeCount = 0;
+ }
+ }
+
+ updateIdle();
+
+#if 0
+ // handle special anims
+ if ((_flags & 4) == 0)
+ return;
+
+
+ if (_animScriptId != -1) {
+ _animationInstance = _vm->getSceneAnimation(this->)
+#endif
+
+ int32 animId = _animSpecialId;
+ if (animId >= 1000)
+ animId = 0;
+
+ if (_animSpecialId < 0)
+ return;
+
+ int32 currentFrame = _animationInstance->getFrame();
+
+ const SpecialCharacterAnimation *anim = getSpecialAnimation(_id, animId);
+
+ if ((_animFlags & 0x10) == 0) {
+ if (_animScriptId != -1 && currentFrame > 0 && !_vm->getSceneAnimationScript(_animScriptId)->_frozen) {
+ if (_vm->getCurrentLineToSay() != _lineToSayId && (_animFlags & 8))
+ stopSpecialAnim();
+ return;
+ }
+
+ if (_id == 1 && (_animFlags & 4)) {
+ if (_animFlags & 0x10)
+ return;
+ } else {
+ if (_id == 1 && (_animFlags & 0x10) && _vm->getCurrentLineToSay() != -1) {
+ return;
+ }
+ if ((_animFlags & 0x40) == 0 && _vm->getCurrentLineToSay() == -1) {
+ stopSpecialAnim();
+ return;
+ }
+
+// Strangerke - Commented (not used)
+#if 0
+ if (_animFlags & 8) {
+ if (anim->_flags7 == 0xff && anim->_flags9 == 0xff) {
+ // start voice
+ }
+ }
+#endif
+
+ if (_animScriptId != -1)
+ _vm->getSceneAnimationScript(_animScriptId)->_frozen = true;
+
+ // TODO setup backup //
+
+ _animFlags |= 0x10;
+ _animationInstance->setFrame(0);
+ _time = _vm->getOldMilli() + 8 * _vm->getTickLength();
+ }
+
+ }
+
+ if ((_animFlags & 3) == 2) {
+ if (_vm->getCurrentLineToSay() != _lineToSayId || !_vm->getAudioManager()->voiceStillPlaying()) // || (_flags & 8)) && _vm->getAudioManager()->voiceStillPlaying())
+ _animFlags |= 1;
+
+// Strangerke - Commented (not used)
+// } else {
+ }
+
+ // label29 :
+ if (_time > _vm->getOldMilli())
+ return;
+
+ int32 animFlag = anim->_unused;
+ int32 nextFrame = currentFrame + 1;
+ int32 nextTime = _time;
+ int32 animDir = 1;
+ if (!animFlag) {
+ if (_animFlags & 1) {
+ if (anim->_flags7 == 0xff) {
+ if (currentFrame > anim->_flag1 / 2)
+ animDir = 1;
+ else
+ animDir = -1;
+ } else {
+ if (currentFrame >= anim->_flags6) {
+ if (currentFrame < anim->_flags7)
+ currentFrame = anim->_flags7;
+ }
+ if (currentFrame > anim->_flags6)
+ animDir = 1;
+ else
+ animDir = -1;
+ }
+
+ nextFrame = currentFrame + animDir;
+ nextTime = _vm->getOldMilli() + 6 * _vm->getTickLength();
+ } else {
+ if (_animFlags & 0x20) {
+ nextFrame = currentFrame - 1;
+ if (nextFrame == anim->_flags6 - 1) {
+ if (anim->_flags8 != 1 && (_vm->randRange(0, 1) == 1 || anim->_flags8 == 2)) {
+ _animFlags &= ~0x20;
+ nextFrame += 2;
+ if (nextFrame > anim->_flags7)
+ nextFrame = anim->_flags7;
+ } else {
+ nextFrame = anim->_flags7;
+ }
+ }
+ } else {
+ nextFrame = currentFrame + 1;
+// Strangerke - Commented (not used)
+#if 0
+ if (!_vm->getAudioManager()->voiceStillPlaying()) {
+ if (_animFlags & 8) {
+ if ((anim->_flags9 == 0xff && nextFrame == anim->_flags6) ||
+ (anim->_flags9 != 0xff && nextFrame >= anim->_flags9)) {
+ // start really talking
+ }
+ }
+ }
+#endif
+ if (nextFrame == anim->_flags7 + 1 && (_animFlags & 0x40) == 0) {
+ if (anim->_flags8 != 1 && (_vm->randRange(0, 1) || anim->_flags8 == 2)) {
+ _animFlags |= 0x20;
+ nextFrame -= 2;
+ if (nextFrame < anim->_flags6)
+ nextFrame = anim->_flags6;
+ } else {
+ nextFrame = anim->_flags6;
+ }
+ }
+ }
+
+ nextTime = _vm->getOldMilli() + 8 * _vm->getTickLength();
+ }
+ // goto label78
+ }
+ // skipped all this part.
+
+ //label78
+#if 0
+ if (_id == 0)
+ debugC(0, 0xfff, " drew animation flag %d / frame %d", _animFlags, nextFrame);
+
+ if (_id == 1)
+ debugC(0, 0xfff, " flux animation flag %d / frame %d", _animFlags, nextFrame);
+
+ if (_id == 7)
+ debugC(0, 0xfff, " footman animation flag %d / frame %d", _animFlags, nextFrame);
+#endif
+
+ _time = nextTime;
+ if (nextFrame < 0 || nextFrame >= anim->_flag1) {
+ if ((_animFlags & 2) == 0 || _vm->getCurrentLineToSay() != _lineToSayId) {
+ stopSpecialAnim();
+ return;
+ }
+
+ // lots skipped here
+
+ _animFlags &= ~0x10;
+ _animationInstance->forceFrame(0);
+ return;
+
+ }
+
+ //if ((_flags & 8) == 0 || !_vm->getAudioManager()->voiceStillPlaying( ) || )
+ _animationInstance->forceFrame(nextFrame);
+}
+
+// adapted from Kyra
+int32 Character::getFacingFromDirection(int32 dx, int32 dy) {
+ debugC(4, kDebugCharacter, "getFacingFromDirection(%d, %d)", dx, dy);
+
+ static const int facingTable[] = {
+ //1, 0, 1, 2, 3, 4, 3, 2, 7, 0, 7, 6, 5, 4, 5, 6
+ 5, 6, 5, 4, 3, 2, 3, 4, 7, 6, 7, 0, 1, 2, 1, 0
+ };
+ dx = -dx;
+
+ int32 facingEntry = 0;
+ int32 ydiff = dy;
+ if (ydiff < 0) {
+ ++facingEntry;
+ ydiff = -ydiff;
+ }
+ facingEntry <<= 1;
+
+ int32 xdiff = dx;
+ if (xdiff < 0) {
+ ++facingEntry;
+ xdiff = -xdiff;
+ }
+
+ facingEntry <<= 1;
+
+ if (xdiff >= ydiff) {
+ int32 temp = ydiff;
+ ydiff = xdiff;
+ xdiff = temp;
+ } else {
+ facingEntry += 1;
+ }
+
+ facingEntry <<= 1;
+
+ int32 temp = (ydiff + 1) >> 1;
+
+ if (xdiff < temp)
+ facingEntry += 1;
+
+ return facingTable[facingEntry];
+}
+
+AnimationInstance *Character::getAnimationInstance() {
+ return _animationInstance;
+}
+
+void Character::setAnimationInstance(AnimationInstance *instance) {
+ _animationInstance = instance;
+}
+
+int32 Character::getScale() {
+ return _scale;
+}
+
+void Character::playWalkAnim(int32 startFrame, int32 endFrame) {
+
+}
+
+void Character::setId(int32 id) {
+ _id = id;
+}
+
+int32 Character::getId() {
+ return _id;
+}
+
+void Character::save(Common::WriteStream *stream) {
+ debugC(1, kDebugCharacter, "save(stream)");
+
+ stream->writeSint32LE(_flags);
+ stream->writeSint32LE(_x);
+ stream->writeSint32LE(_y);
+ stream->writeSint32LE(_z);
+ stream->writeSint32LE(_finalX);
+ stream->writeSint32LE(_finalY);
+ stream->writeSint32LE(_scale);
+ stream->writeSint32LE(_id);
+
+ stream->writeSint32LE(_animScriptId);
+ stream->writeSint32LE(_animFlags);
+ stream->writeSint32LE(_animSpecialDefaultId);
+ stream->writeSint32LE(_sceneAnimationId);
+}
+
+void Character::load(Common::ReadStream *stream) {
+ debugC(1, kDebugCharacter, "read(stream)");
+
+ _flags = stream->readSint32LE();
+ _flags &= ~1; // characters are not walking when restoring.
+
+ _x = stream->readSint32LE();
+ _y = stream->readSint32LE();
+ _z = stream->readSint32LE();
+ _finalX = stream->readSint32LE();
+ _finalY = stream->readSint32LE();
+ _scale = stream->readSint32LE();
+ _id = stream->readSint32LE();
+
+ _animScriptId = stream->readSint32LE();
+ _animFlags = stream->readSint32LE();
+ _animSpecialDefaultId = stream->readSint32LE();
+ _sceneAnimationId = stream->readSint32LE();
+
+ if (_sceneAnimationId > -1) {
+ setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance);
+ }
+}
+
+void Character::setAnimScript(int32 animScriptId) {
+ _animScriptId = animScriptId;
+}
+
+void Character::setSceneAnimationId(int32 sceneAnimationId) {
+ _sceneAnimationId = sceneAnimationId;
+}
+
+int32 Character::getAnimScript() {
+ return _animScriptId;
+}
+
+void Character::playTalkAnim() {
+
+}
+
+void Character::stopWalk() {
+ debugC(1, kDebugCharacter, "stopWalk()");
+
+ _finalX = _x;
+ _finalY = _y;
+ _flags &= ~0x1;
+ _currentPathNode = 0;
+ _currentPathNodeCount = 0;
+}
+
+const SpecialCharacterAnimation *Character::getSpecialAnimation(int32 characterId, int32 animationId) {
+ debugC(6, kDebugCharacter, "getSpecialAnimation(%d, %d)", characterId, animationId);
+
+ // very nice animation list hardcoded in the executable...
+ static const SpecialCharacterAnimation anims[] = {
+ { "TLK547_?", 9, 0, 0, 0, 0, 0, 1, 5, 8, 1, 8, 0, 255 },
+ { "TLK555_?", 16, 0, 0, 0, 0, 6, 8, 10, 255, 6, 11, 2, 255 },
+ { "LST657_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 255 },
+ { "TLK587_?", 18, 0, 0, 0, 0, 5, 7, 9, 11, 4, 13, 1, 255 },
+ { "LST659_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 0, 255 },
+ { "TLK595_?", 11, 0, 0, 0, 0, 3, 6, 255, 255, 1, 7, 0, 255 },
+ { "IDL165_?", 13, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 0, 255 },
+ { "LST699_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 1, 255 },
+ { "LST713_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 255 },
+ { "IDL169_?", 16, 0, 0, 0, 0, 255, 255, 255, 255, 5, 9, 2, 255 },
+ { "IDL173_?", 19, 0, 0, 0, 0, 255, 255, 255, 255, 4, 17, 1, 255 },
+ { "IDL187_?", 14, 0, 0, 0, 0, 255, 255, 255, 255, 4, 8, 0, 255 },
+ { "IDL185_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 9, 1, 255 },
+ { "TLK635_?", 16, 0, 0, 0, 0, 5, 8, 10, 12, 4, 12, 0, 255 },
+ { "TLK637_?", 18, 0, 0, 0, 0, 5, 7, 9, 12, 4, 13, 0, 255 },
+ { "TLK551_?", 20, 0, 0, 0, 0, 5, 9, 11, 15, 4, 16, 0, 255 },
+ { "TLK553_?", 20, 0, 0, 0, 0, 7, 9, 11, 13, 6, 15, 0, 255 },
+ { "TLK619_?", 18, 0, 0, 0, 0, 5, 8, 11, 13, 5, 15, 0, 255 },
+ { "TLK601_?", 12, 0, 0, 0, 0, 2, 5, 6, 10, 2, 10, 1, 255 },
+ { "TLK559_?", 18, 0, 0, 0, 0, 4, 6, 10, 12, 4, 13, 0, 255 },
+ { "TLK557_?", 16, 0, 0, 0, 0, 6, 8, 10, 255, 6, 11, 0, 255 },
+ { "TLK561_?", 17, 0, 0, 0, 0, 6, 8, 10, 12, 5, 12, 0, 255 },
+ { "TLK623_?", 19, 0, 0, 0, 0, 6, 8, 10, 13, 6, 14, 0, 255 },
+ { "TLK591_?", 20, 0, 0, 0, 0, 10, 14, 255, 255, 7, 15, 0, 255 },
+ { "TLK567_?", 19, 0, 0, 0, 0, 6, 9, 11, 14, 5, 15, 0, 255 },
+ { "TLK629_?", 18, 0, 0, 0, 0, 6, 8, 10, 11, 6, 12, 0, 255 },
+ { "TLK627_?", 19, 0, 0, 0, 0, 7, 10, 12, 14, 4, 14, 0, 255 },
+ { "TLK631_?", 19, 0, 0, 0, 0, 8, 10, 255, 255, 8, 12, 0, 255 },
+ { "TLK565_?", 17, 0, 0, 0, 0, 4, 7, 9, 11, 3, 12, 0, 255 },
+ { "TLK603_?", 16, 0, 0, 0, 0, 5, 255, 255, 255, 3, 9, 0, 255 },
+ { "TLK573_?", 20, 0, 0, 0, 0, 6, 7, 10, 255, 6, 16, 2, 255 },
+ { "TLK615_?", 17, 0, 0, 0, 0, 6, 8, 10, 12, 5, 12, 0, 255 },
+ { "TLK609_?", 18, 0, 0, 0, 0, 6, 8, 10, 12, 5, 13, 0, 255 },
+ { "TLK611_?", 18, 0, 0, 0, 0, 8, 10, 12, 255, 7, 13, 0, 255 },
+ { "TLK607_?", 16, 0, 0, 0, 0, 4, 7, 9, 11, 4, 12, 0, 255 },
+ { "TLK581_?", 15, 0, 0, 0, 0, 7, 9, 11, 255, 6, 11, 0, 255 },
+ { "SHD107_?", 46, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "IHL106_?", 23, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 7 },
+ { "GLV106_?", 23, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 7 },
+ { "FXTKA_?", 11, 0, 0, 0, 0, 7, 255, 255, 255, 2, 9, 0, 255 },
+ { "FXTKF_?", 12, 0, 0, 0, 0, 6, 8, 255, 255, 5, 9, 0, 255 },
+ { "FXTKG_?", 9, 0, 0, 0, 0, 5, 255, 255, 255, 4, 7, 0, 255 },
+ { "FXTKI_?", 12, 0, 0, 0, 0, 6, 255, 255, 255, 5, 9, 0, 255 },
+ { "FXTKL_?", 14, 0, 0, 0, 0, 4, 6, 255, 255, 3, 10, 0, 255 },
+ { "FXTKO_?", 10, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 255 },
+ { "FXTKP_?", 9, 0, 0, 0, 0, 4, 6, 255, 255, 3, 7, 0, 255 },
+ { "FXTKQ_?", 10, 0, 0, 0, 0, 4, 6, 255, 255, 3, 7, 0, 255 },
+ { "FXLSA_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 255 },
+ { "FXLSB_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 },
+ { "FXLSK_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 5, 6, 0, 255 },
+ { "FXLSM_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 },
+ { "FXLSP_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 0, 255 },
+ { "FXLSQ_?", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 0, 255 },
+ { "FXIDE_?", 10, 0, 0, 0, 0, 255, 255, 255, 255, 5, 7, 0, 255 },
+ { "FXIDI_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 1, 6, 1, 255 },
+ { "FXRCT1_?", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FXTKB_?", 11, 0, 0, 0, 0, 7, 255, 255, 255, 5, 9, 0, 255 },
+ { "FXTKC_?", 14, 0, 0, 0, 0, 2, 5, 8, 10, 1, 12, 2, 255 },
+ { "FXTKD_?", 14, 0, 0, 0, 0, 5, 7, 9, 255, 4, 11, 0, 255 },
+ { "FXTKE_?", 14, 0, 0, 0, 0, 2, 255, 255, 255, 1, 12, 1, 255 },
+ { "FXTKH_?", 11, 0, 0, 0, 0, 6, 8, 255, 255, 4, 9, 0, 255 },
+ { "FXTKJ_?", 8, 0, 0, 0, 0, 7, 255, 255, 255, 4, 7, 0, 255 },
+ { "FXTKK_?", 13, 0, 0, 0, 0, 6, 8, 255, 255, 5, 9, 0, 255 },
+ { "FXTKM_?", 11, 0, 0, 0, 0, 6, 255, 255, 255, 4, 7, 0, 255 },
+ { "FXTKN_?", 9, 0, 0, 0, 0, 5, 7, 255, 255, 4, 7, 0, 255 },
+ { "FXLSC_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 1, 255 },
+ { "FXLSD_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 },
+ { "FXLSE_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 8, 8, 0, 255 },
+ { "FXLSG_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 6, 8, 2, 255 },
+ { "FXLSI_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 5, 6, 0, 255 },
+ { "FXLSJ_?", 5, 0, 0, 0, 0, 255, 255, 255, 255, 3, 4, 0, 255 },
+ { "FXLSO_?", 8, 0, 0, 0, 0, 255, 255, 255, 255, 4, 5, 0, 255 },
+ { "FXIDA_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 1, 12, 1, 255 },
+ { "FXIDB_?", 12, 0, 0, 0, 0, 255, 255, 255, 255, 4, 11, 1, 255 },
+ { "FXIDC_?", 11, 0, 0, 0, 0, 255, 255, 255, 255, 7, 7, 0, 255 },
+ { "FXIDD_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 6, 0, 255 },
+ { "FXIDG_?", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 4, 0, 255 },
+ { "FXVRA_?", 7, 0, 0, 0, 0, 255, 255, 255, 255, 2, 6, 2, 255 },
+ { "FXIDF_?", 15, 0, 0, 0, 0, 255, 255, 255, 255, 9, 11, 0, 255 },
+ { "FXEXA_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 5, 5, 0, 255 },
+ { "FXEXA_?", 9, 0, 0, 0, 0, 255, 255, 255, 255, 5, 5, 0, 255 },
+ { "FFNTK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 },
+ { "FFTLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 1 },
+ { "FFBLS1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 2 },
+ { "FFLOV2", 6, 0, 0, 0, 0, 255, 255, 255, 255, 3, 5, 0, 2 },
+ { "FFWOE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 9, 0, 2 },
+ { "FFSNF1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 4, 6, 0, 4 },
+ { "FFLAF1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 8, 0, 1 },
+ { "FFSKE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 0, 2 },
+ { "RGTLK2", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 },
+ { "RGTLK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 },
+ { "BRTLK1", 26, 0, 0, 0, 0, 255, 255, 255, 255, 2, 23, 0, 255 },
+ { "BREXT1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRLRT1", 19, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 },
+ { "BRBWV1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 255 },
+ { "BRPAT1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRBSP1", 7, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRBEX1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRBLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRBET1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRWEX1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BBTLK2", 26, 0, 0, 0, 0, 255, 255, 255, 255, 2, 23, 1, 255 },
+ { "BBEXT2", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 },
+ { "BRLST1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 },
+ { "BRLSN1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 1, 13, 2, 255 },
+ { "BRBNO1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BRBND1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BBLSN2", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "CCTALK", 6, 0, 0, 0, 0, 2, 5, 255, 255, 1, 5, 0, 255 },
+ { "CCBIT1", 13, 0, 0, 0, 0, 3, 5, 9, 11, 2, 11, 2, 255 },
+ { "CCCMP1", 13, 0, 0, 0, 0, 6, 9, 255, 255, 5, 10, 1, 2 },
+ { "CCCOY1", 14, 0, 0, 0, 0, 6, 8, 255, 255, 4, 8, 0, 3 },
+ { "CCFNG1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 },
+ { "CCGRB1", 13, 0, 0, 0, 0, 6, 255, 255, 255, 6, 9, 0, 3 },
+ { "CCGST1", 9, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 2 },
+ { "CCHCN1", 10, 0, 0, 0, 0, 6, 9, 255, 255, 4, 9, 0, 0 },
+ { "CCHND1", 7, 0, 0, 0, 0, 6, 255, 255, 255, 2, 6, 0, 1 },
+ { "FTTLK2", 11, 0, 0, 0, 0, 1, 4, 6, 9, 1, 10, 0, 2 },
+ { "FTGNO2", 11, 0, 0, 0, 0, 4, 6, 8, 255, 4, 8, 1, 2 },
+ { "FTGST2", 6, 0, 0, 0, 0, 1, 2, 4, 5, 2, 5, 0, 1 },
+ { "FTHND2", 7, 0, 0, 0, 0, 2, 5, 255, 255, 1, 6, 1, 255 },
+ { "FTRNT2", 11, 0, 0, 0, 0, 3, 5, 7, 9, 2, 9, 1, 1 },
+ { "FTSRG2", 10, 0, 0, 0, 0, 4, 6, 8, 255, 3, 8, 1, 1 },
+ { "FTQOT2", 8, 0, 0, 0, 0, 1, 4, 8, 255, 1, 6, 1, 255 },
+ { "FMSTK1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 },
+ { "FMCRH1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 0, 255 },
+ { "FMFGR1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 1, 10, 0, 255 },
+ { "FMPRS1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 1, 14, 0, 255 },
+ { "FMAGR1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 0, 255 },
+ { "FMWOE1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 255 },
+ { "FMTOE1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FM1TK1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FM2TK1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FM3TK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FMTNB1", 4, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FMLOK1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "FMCST1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 255 },
+ { "FMLUP3", 8, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 255 },
+ { "BDTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 255 },
+ { "BDGLE1", 15, 0, 0, 0, 0, 255, 255, 255, 255, 6, 10, 0, 255 },
+ { "BDSHK1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 1 },
+ { "BDWOE1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 9, 16, 0, 2 },
+ { "BDHIP1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 8, 16, 0, 1 },
+ { "BDFLG1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "BDKLT1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 0, 255 },
+ { "BDSWY1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "WPSNK1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "WPLAF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 5, 9, 1, 1 },
+ { "DOTLK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 0, 255 },
+ { "DOGST1", 15, 0, 0, 0, 0, 255, 255, 255, 255, 4, 11, 1, 255 },
+ { "DO2DF1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 3, 11, 1, 255 },
+ { "DOSNG1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 8, 9, 1, 255 },
+ { "DOWOE1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 1, 255 },
+ { "DO2ME1", 18, 0, 0, 0, 0, 255, 255, 255, 255, 5, 13, 1, 255 },
+ { "DOGLP1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 },
+ { "DOCRY1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 1, 255 },
+ { "METLK1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 1, 4, 0, 255 },
+ { "MECHT1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 1, 255 },
+ { "ME2DF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 0, 255 },
+ { "MESNG1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 2, 255 },
+ { "MEWOE1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 3, 10, 1, 255 },
+ { "ME2DO1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 2, 9, 1, 255 },
+ { "MEGLP1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 1, 255 },
+ { "MECRY1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 6, 9, 1, 255 },
+ { "CSTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 0 },
+ { "CSNUD1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 5, 11, 0, 2 },
+ { "CSSPR1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 4, 8, 0, 2 },
+ { "CSWVE1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 4, 9, 0, 1 },
+ { "CSYEL1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 2, 6, 0, 1 },
+ { "JMTLK1", 7, 0, 0, 0, 0, 1, 4, 255, 255, 1, 6, 0, 0 },
+ { "JMEGO1", 11, 0, 0, 0, 0, 6, 255, 255, 255, 3, 8, 0, 1 },
+ { "JMARS1", 7, 0, 0, 0, 0, 4, 6, 255, 255, 3, 6, 0, 2 },
+ { "JMHIP1", 8, 0, 0, 0, 0, 3, 5, 7, 255, 2, 7, 0, 1 },
+ { "JMBNK1", 2, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "MRTLK1", 9, 0, 0, 0, 0, 4, 7, 255, 255, 2, 7, 0, 1 },
+ { "MRHOF1", 8, 0, 0, 0, 0, 3, 5, 255, 255, 2, 5, 0, 255 },
+ { "MRMRN1", 11, 0, 0, 0, 0, 3, 7, 255, 255, 1, 8, 0, 0 },
+ { "MRDPR1", 11, 0, 0, 0, 0, 1, 5, 9, 255, 1, 8, 0, 255 },
+ { "MRGLE1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 },
+ { "MRTDF1", 11, 0, 0, 0, 0, 3, 7, 9, 255, 3, 9, 0, 1 },
+ { "MREDF1", 11, 0, 0, 0, 0, 4, 255, 255, 255, 1, 10, 1, 255 },
+ { "MREPL1", 12, 0, 0, 0, 0, 5, 6, 7, 9, 2, 9, 1, 1 },
+ { "MRAPL1", 12, 0, 0, 0, 0, 4, 8, 9, 255, 2, 9, 0, 1 },
+ { "MREVL1", 8, 0, 0, 0, 0, 5, 255, 255, 255, 1, 5, 1, 255 },
+ { "BWDMR1", 16, 0, 0, 0, 0, 4, 7, 9, 11, 3, 14, 0, 1 },
+ { "BWBUF1", 12, 0, 0, 0, 0, 5, 8, 255, 255, 3, 11, 0, 1 },
+ { "BWHIP1", 12, 0, 0, 0, 0, 3, 6, 255, 255, 1, 9, 2, 0 },
+ { "BWHWL1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 1, 4, 2, 255 },
+ { "BWLEN1", 10, 0, 0, 0, 0, 3, 6, 255, 255, 2, 7, 0, 1 },
+ { "BWSRL1", 6, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 1 },
+ { "BWWAG1", 6, 0, 0, 0, 0, 4, 10, 14, 18, 1, 4, 0, 0 },
+ { "BWYEL1", 8, 0, 0, 0, 0, 4, 255, 255, 255, 2, 7, 0, 1 },
+ { "BWTLK1", 15, 0, 0, 0, 0, 5, 8, 255, 255, 5, 9, 0, 1 },
+ { "SLTLK1", 19, 0, 0, 0, 0, 255, 255, 255, 255, 1, 18, 0, 255 },
+ { "SLPND1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SLPNT1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SLPTR1", 14, 0, 0, 0, 0, 255, 255, 255, 255, 6, 13, 1, 255 },
+ { "SDTLK1", 7, 0, 0, 0, 0, 255, 255, 255, 255, 1, 5, 0, 255 },
+ { "SDPDF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 3, 6, 0, 255 },
+ { "SDPNT1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 },
+ { "SDSLF1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 2, 7, 0, 255 },
+ { "SDSTG1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SDWVE1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 0, 255 },
+ { "SDSTK1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SDSMK1", 22, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SDGLN1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SDLAF1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "RMHIP1", 12, 0, 0, 0, 0, 7, 255, 255, 255, 1, 10, 2, 255 },
+ { "RMGES1", 19, 0, 0, 0, 0, 11, 255, 255, 255, 8, 13, 2, 2 },
+ { "RMPCH1", 18, 0, 0, 0, 0, 12, 255, 255, 255, 6, 13, 0, 2 },
+ { "RMSTH1", 12, 0, 0, 0, 0, 5, 255, 255, 255, 3, 6, 0, 2 },
+ { "RMHND1", 7, 0, 0, 0, 0, 5, 255, 255, 255, 5, 5, 1, 255 },
+ { "RMSTH1", 12, 0, 0, 0, 0, 5, 255, 255, 255, 5, 6, 1, 2 },
+ { "SGHND1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 9, 0, 0 },
+ { "SGSTF1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 1, 12, 0, 255 },
+ { "SGSLP1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 },
+ { "SGPHC1", 12, 0, 0, 0, 0, 255, 255, 255, 255, 4, 9, 0, 255 },
+ { "SGHALT", 22, 0, 0, 0, 0, 255, 255, 255, 255, 7, 15, 0, 255 },
+ { "STTLK1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 },
+ { "STTNM1", 13, 0, 0, 0, 0, 5, 9, 255, 255, 3, 10, 0, 2 },
+ { "STFST1", 11, 0, 0, 0, 0, 3, 8, 255, 255, 1, 9, 0, 255 },
+ { "STLAF1", 20, 0, 0, 0, 0, 255, 255, 255, 255, 11, 15, 1, 2 },
+ { "STGES1", 13, 0, 0, 0, 0, 5, 7, 255, 255, 3, 7, 0, 2 },
+ { "STFNT1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 255, 255, 0, 2 },
+ { "STSRK1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 0 },
+ { "STRED1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 2, 10, 0, 255 },
+ { "STLKU1", 6, 0, 0, 0, 0, 3, 255, 255, 255, 2, 5, 0, 0 },
+ { "STKEY1", 15, 0, 0, 0, 0, 9, 11, 255, 255, 9, 14, 0, 255 },
+ { "STMKTD1", 7, 0, 0, 0, 0, 3, 6, 255, 255, 1, 6, 0, 255 },
+ { "STTKM1", 21, 0, 0, 0, 0, 12, 13, 15, 16, 12, 17, 0, 1 },
+ { "STMSZ1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 255 },
+ { "STPNV1", 14, 0, 0, 0, 0, 6, 11, 255, 255, 4, 11, 0, 1 },
+ { "STSOM1", 10, 0, 0, 0, 0, 255, 255, 255, 255, 1, 3, 2, 255 },
+ { "MYTLK1", 9, 0, 0, 0, 0, 2, 4, 255, 255, 1, 4, 0, 0 },
+ { "MYSQUAWK", 5, 0, 0, 0, 0, 255, 255, 255, 255, 3, 3, 1, 255 },
+ { "SPTLK", 12, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPARM", 16, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPHOP", 18, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPLNT", 16, 0, 0, 0, 0, 255, 255, 255, 255, 3, 13, 0, 255 },
+ { "SPLAF", 11, 0, 0, 0, 0, 255, 255, 255, 255, 5, 10, 2, 255 },
+ { "SPTFN", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPPIN", 14, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPINH1", 21, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "SPSFTCOM", 10, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 255 },
+ { "MFTMZ1", 9, 0, 0, 0, 0, 255, 255, 255, 255, 1, 8, 1, 255 },
+ { "MFTLK1", 13, 0, 0, 0, 0, 2, 7, 255, 255, 1, 12, 1, 255 },
+ { "VGCIR1", 15, 0, 0, 0, 0, 5, 9, 255, 255, 2, 13, 1, 255 },
+ { "VGBIT1", 12, 0, 0, 0, 0, 6, 9, 255, 255, 2, 9, 1, 255 },
+ { "VGANG1", 10, 0, 0, 0, 0, 9, 255, 255, 255, 1, 9, 0, 255 },
+ { "VGCOM1", 13, 0, 0, 0, 0, 5, 11, 255, 255, 2, 11, 0, 255 },
+ { "VGCUR1", 8, 0, 0, 0, 0, 4, 8, 255, 255, 2, 7, 0, 255 },
+ { "VGTLK1", 11, 0, 0, 0, 0, 3, 6, 255, 255, 3, 10, 0, 255 },
+ { "VGEXP1", 10, 0, 0, 0, 0, 5, 9, 255, 255, 3, 9, 0, 255 },
+ { "WFTLK1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 1, 7, 0, 1 },
+ { "WFPNT1", 20, 0, 0, 0, 0, 255, 255, 255, 255, 6, 16, 0, 1 },
+ { "WFFST1", 13, 0, 0, 0, 0, 255, 255, 255, 255, 2, 8, 0, 2 },
+ { "WFTNO1", 8, 0, 0, 0, 0, 255, 255, 255, 255, 2, 5, 0, 2 },
+ { "WFSRG1", 11, 0, 0, 0, 0, 255, 255, 255, 255, 3, 8, 0, 1 },
+ { "WFGTK1", 16, 0, 0, 0, 0, 255, 255, 255, 255, 1, 15, 0, 255 },
+ { "WFPAW1", 24, 0, 0, 0, 0, 255, 255, 255, 255, 4, 22, 0, 1 },
+ { "LGTLK", 20, 0, 0, 0, 0, 4, 8, 11, 15, 1, 17, 0, 255 },
+ { "LGSHOUT", 16, 0, 0, 0, 0, 12, 255, 255, 255, 6, 12, 0, 255 },
+ { "POMRN1", 12, 0, 0, 0, 0, 3, 5, 7, 255, 3, 9, 0, 2 },
+ { "POGLE1", 14, 0, 0, 0, 0, 7, 10, 255, 255, 5, 10, 0, 2 },
+ { "PLMRG1", 16, 0, 0, 0, 0, 9, 255, 255, 255, 8, 12, 0, 1 },
+ { "PLCMR1", 16, 0, 0, 0, 0, 8, 10, 255, 255, 8, 12, 0, 3 },
+ { "PLEVL1", 17, 0, 0, 0, 0, 9, 255, 255, 255, 7, 9, 0, 1 },
+ { "PLEDF1", 9, 0, 0, 0, 0, 4, 6, 255, 255, 5, 7, 0, 2 },
+ { "PLTLK1", 11, 0, 0, 0, 0, 5, 8, 255, 255, 5, 8, 0, 1 },
+ { "ELTLK1", 8, 0, 0, 0, 0, 3, 5, 7, 255, 2, 7, 0, 255 },
+ { "ELSNR1", 7, 0, 0, 0, 0, 3, 255, 255, 255, 1, 5, 0, 255 },
+ { "RG2TK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 },
+ { "RG2TK1", 10, 0, 0, 0, 0, 4, 6, 255, 255, 2, 6, 0, 1 },
+ { "C2TALK", 6, 0, 0, 0, 0, 2, 5, 255, 255, 1, 5, 0, 255 },
+ { "C2BIT1", 13, 0, 0, 0, 0, 3, 5, 9, 11, 2, 11, 2, 255 },
+ { "C2CMP1", 13, 0, 0, 0, 0, 6, 9, 255, 255, 5, 10, 1, 2 },
+ { "C2COY1", 14, 0, 0, 0, 0, 6, 8, 255, 255, 4, 8, 0, 3 },
+ { "C2FNG1", 5, 0, 0, 0, 0, 255, 255, 255, 255, 4, 4, 0, 255 },
+ { "C2GRB1", 13, 0, 0, 0, 0, 6, 255, 255, 255, 6, 9, 0, 3 },
+ { "C2GST1", 9, 0, 0, 0, 0, 4, 255, 255, 255, 4, 7, 0, 2 },
+ { "C2HCN1", 10, 0, 0, 0, 0, 6, 9, 255, 255, 4, 9, 0, 0 },
+ { "C2HND1", 7, 0, 0, 0, 0, 6, 255, 255, 255, 2, 6, 0, 1 },
+ { "666TKBB3", 21, 0, 0, 0, 0, 9, 14, 255, 255, 6, 16, 0, 255 },
+ { "665TFLX3", 27, 0, 0, 0, 0, 10, 14, 17, 255, 10, 18, 0, 255 },
+ { "664FXTK3", 18, 0, 0, 0, 0, 5, 7, 11, 13, 3, 15, 0, 255 },
+ { "FDTALK", 15, 0, 0, 0, 0, 9, 255, 255, 255, 7, 9, 0, 255 },
+ { "FDYELL", 16, 0, 0, 0, 0, 10, 255, 255, 255, 8, 10, 0, 255 },
+ { "GLTLK", 20, 0, 0, 0, 0, 6, 12, 18, 255, 1, 19, 0, 255 },
+ { "GLTRN", 4, 0, 0, 0, 0, 3, 255, 255, 255, 1, 2, 0, 255 },
+ { "RAYTALK1", 10, 0, 0, 0, 0, 3, 5, 8, 255, 1, 9, 0, 255 },
+ { "BRTKB1", 17, 0, 0, 0, 0, 255, 255, 255, 255, 2, 14, 0, 255 }
+ };
+
+ static const int32 characterAnims[] = {
+ 0, 39, 81, 89, 91, 108, 117, 124, 138, 146,
+ 148, 156, 164, 169, 174, 179, 184, 193, 197, 207,
+ 213, 218, 233, 235, 244, 245, 246, 246, 246, 246,
+ 253, 253, 253, 253, 260, 262, 264, 269, 271, 273,
+ 282, 284, 285, 287, 289, 290, 291, 291, 291, 291,
+ 289, 289, 289, 289, 289, 289, 289, 289, 289, 289
+ };
+
+ return &anims[characterAnims[characterId] + animationId];
+}
+
+bool Character::loadShadowAnimation(Common::String animName) {
+ debugC(1, kDebugCharacter, "loadShadowAnimation(%s)", animName.c_str());
+
+ _shadowAnim = new Animation(_vm);
+ if (!_shadowAnim->loadAnimation(animName))
+ return false;
+
+ _shadowAnimationInstance = _vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
+ _vm->getAnimationManager()->addInstance(_shadowAnimationInstance);
+ _shadowAnimationInstance->setAnimation(_shadowAnim);
+ _shadowAnimationInstance->setVisible(true);
+ _shadowAnimationInstance->setUseMask(true);
+
+ return true;
+}
+
+void Character::playAnim(int32 animId, int32 unused, int32 flags) {
+ debugC(3, kDebugCharacter, "playAnim(%d, unused, %d)", animId, flags);
+
+ if (animId == 0)
+ animId = _animSpecialDefaultId;
+
+ // get the anim to load
+ const SpecialCharacterAnimation *anim = getSpecialAnimation(_id, animId);
+
+ char animName[20];
+ strcpy(animName, anim->_filename);
+
+ int32 facing = _facing;
+ if (_id == 1) {
+ // flux special case... some animations are not for every facing
+ facing = CharacterFlux::fixFacingForAnimation(facing, animId);
+ }
+
+ if (strchr(animName, '?'))
+ *strchr(animName, '?') = '0' + facing;
+ strcat(animName, ".CAF");
+
+
+ if (_animScriptId != -1)
+ _vm->getSceneAnimationScript(_animScriptId)->_frozen = true;
+
+ if (_sceneAnimationId > -1)
+ setAnimationInstance(_vm->getSceneAnimation(_sceneAnimationId)->_animInstance);
+
+ stopSpecialAnim();
+
+ if (flags & 8) {
+ // talker
+ _lineToSayId = _vm->getCurrentLineToSay();
+
+ // make the talker busy
+ _flags |= 1;
+ }
+ _animFlags |= flags;
+
+ if (_specialAnim)
+ delete _specialAnim;
+ _specialAnim = new Animation(_vm);
+ _specialAnim->loadAnimation(animName);
+
+ _animSpecialId = animId;
+
+ _animationInstance->setAnimation(_specialAnim);
+ _animationInstance->setAnimationRange(0, _specialAnim->_numFrames - 1);
+ _animationInstance->reset();
+ _animationInstance->stopAnimation();
+ _animationInstance->setLooping(false);
+}
+
+int32 Character::getAnimFlag() {
+ return _animFlags;
+}
+
+void Character::setAnimFlag(int32 flag) {
+ _animFlags = flag;
+}
+
+int32 Character::getSceneAnimationId() {
+ return _sceneAnimationId;
+}
+
+void Character::setDefaultSpecialAnimationId(int32 defaultAnimationId) {
+ _animSpecialDefaultId = defaultAnimationId;
+}
+
+int32 Character::getFinalX() {
+ return _finalX;
+}
+
+int32 Character::getFinalY() {
+ return _finalY;
+}
+
+void Character::updateIdle() {
+ debugC(5, kDebugCharacter, "updateIdle()");
+
+ // only flux and drew
+ if (_id > 1)
+ return;
+
+ if (_vm->state()->_mouseHidden)
+ _nextIdleTime = _vm->getOldMilli() + (300 + _vm->randRange(0, 600)) * _vm->getTickLength();
+
+ if (_vm->getOldMilli() > _nextIdleTime) {
+ if (((_flags & 1) == 0) || ((_flags & 2) != 0)) {
+ if (!_vm->state()->_inCloseUp && !_vm->state()->_inCutaway && _animSpecialId == -1) {
+ if (!_vm->state()->_mouseHidden) {
+ _nextIdleTime = _vm->getOldMilli() + (300 + _vm->randRange(0, 600)) * _vm->getTickLength();
+ playAnim(getRandomIdleAnim(), 0, 0x40);
+ _flags |= 0x4;
+ }
+ }
+ }
+ }
+}
+} // End of namespace Toon
+
diff --git a/engines/toon/character.h b/engines/toon/character.h
new file mode 100644
index 0000000000..997a401403
--- /dev/null
+++ b/engines/toon/character.h
@@ -0,0 +1,144 @@
+/* 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 TOON_CHARACTER_H
+#define TOON_CHARACTER_H
+
+#include "toon/toon.h"
+
+namespace Toon {
+
+class ToonEngine;
+
+struct SpecialCharacterAnimation {
+ char _filename[9]; // 0
+ byte _flag1; // 9
+ short _offsetX; // 10
+ short _offsetY; // 12
+ short _unused; // 14
+ short _unused2; // 16
+ byte _flags2; // 18
+ byte _flags3; // 19
+ byte _flags4; // 20
+ byte _flags5; // 21
+ byte _flags6; // 22
+ byte _flags7; // 23
+ byte _flags8; // 24
+ byte _flags9; // 25
+};
+
+
+class Character {
+public:
+ Character(ToonEngine *vm);
+ virtual ~Character(void);
+ virtual void init();
+ virtual int32 getId();
+ virtual void setId(int32 id);
+ virtual void setFacing(int32 facing);
+ virtual int32 getFacing();
+ virtual void setAnimScript(int32 animScriptId);
+ virtual void setSceneAnimationId(int32 sceneAnimationId);
+ virtual void setDefaultSpecialAnimationId(int32 defaultAnimationId);
+ virtual int32 getAnimScript();
+ virtual int32 getSceneAnimationId();
+ virtual void setFlag(int flag);
+ virtual int32 getFlag();
+ virtual int32 getAnimFlag();
+ virtual void setAnimFlag(int32 flag);
+ virtual void setPosition(int32 x, int32 y);
+ virtual int32 getX();
+ virtual int32 getY();
+ virtual int32 getFinalX();
+ virtual int32 getFinalY();
+ virtual bool walkTo(int32 newPosX, int32 newPosY);
+ virtual bool getVisible();
+ virtual void setVisible(bool visible);
+ virtual bool loadWalkAnimation(Common::String animName);
+ virtual bool loadIdleAnimation(Common::String animName);
+ virtual bool loadTalkAnimation(Common::String animName);
+ virtual bool loadShadowAnimation(Common::String animName);
+ virtual bool setupPalette();
+ virtual void playStandingAnim();
+ virtual void playWalkAnim(int32 start, int32 end);
+ virtual void playTalkAnim();
+ virtual void playAnim(int32 animId, int32 unused, int32 flags);
+ virtual void update(int32 timeIncrement);
+ virtual int32 getScale();
+ virtual AnimationInstance *getAnimationInstance();
+ virtual void setAnimationInstance(AnimationInstance *instance);
+ virtual void save(Common::WriteStream *stream);
+ virtual void load(Common::ReadStream *stream);
+ virtual void stopWalk();
+ virtual void stopSpecialAnim();
+ virtual void updateIdle();
+ virtual int32 getRandomIdleAnim() { return 0; }
+
+ int32 getFacingFromDirection(int32 dx, int32 dy);
+ static const SpecialCharacterAnimation *getSpecialAnimation(int32 characterId, int32 animationId);
+
+
+protected:
+ ToonEngine *_vm;
+
+ int32 _id;
+ int32 _animScriptId;
+ int32 _animSpecialId;
+ int32 _animSpecialDefaultId;
+ int32 _sceneAnimationId;
+ int32 _lineToSayId;
+ int32 _time;
+ int32 _x;
+ int32 _y;
+ int32 _z;
+ int32 _finalX;
+ int32 _finalY;
+ int32 _facing;
+ int32 _flags;
+ int32 _animFlags;
+ int32 _scale;
+ int32 _nextIdleTime;
+ bool _visible;
+ bool _blockingWalk;
+ int32 _speed;
+ int32 _lastWalkTime;
+ int32 _numPixelToWalk;
+
+ AnimationInstance *_animationInstance;
+ AnimationInstance *_shadowAnimationInstance;
+ Animation *_walkAnim;
+ Animation *_idleAnim;
+ Animation *_talkAnim;
+ Animation *_shadowAnim;
+ Animation *_specialAnim;
+
+ int32 _currentPathX[4096];
+ int32 _currentPathY[4096];
+ int32 _currentPathNodeCount;
+ int32 _currentPathNode;
+};
+
+} // End of namespace Toon
+#endif
diff --git a/engines/toon/conversation.cpp b/engines/toon/conversation.cpp
new file mode 100644
index 0000000000..4678ccc1c8
--- /dev/null
+++ b/engines/toon/conversation.cpp
@@ -0,0 +1,49 @@
+/* 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 "toon/conversation.h"
+
+namespace Toon {
+
+void Conversation::save(Common::WriteStream *stream, int16 *conversationDataBase) {
+ stream->writeSint32BE(_enable);
+ for (int32 i = 0; i < 10; i++) {
+ stream->writeSint32BE(state[i]._data2);
+ stream->writeSint16BE(state[i]._data3);
+ stream->writeSint32BE((int16 *)state[i]._data4 - conversationDataBase);
+ }
+}
+
+void Conversation::load(Common::ReadStream *stream, int16 *conversationDataBase) {
+ _enable = stream->readSint32BE();
+ for (int32 i = 0; i < 10; i++) {
+ state[i]._data2 = stream->readSint32BE();
+ state[i]._data3 = stream->readSint16BE();
+ state[i]._data4 = conversationDataBase + stream->readSint32BE();
+ }
+}
+
+
+}
diff --git a/engines/toon/conversation.h b/engines/toon/conversation.h
new file mode 100644
index 0000000000..784c681055
--- /dev/null
+++ b/engines/toon/conversation.h
@@ -0,0 +1,50 @@
+/* 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 TOON_CONVERSATION_H
+#define TOON_CONVERSATION_H
+
+#include "engines/engine.h"
+#include "common/stream.h"
+
+namespace Toon {
+
+class Conversation {
+public:
+ int32 _enable; // 00
+
+ struct ConvState {
+ int32 _data2; // 04
+ int16 _data3; // 08
+ void *_data4; // 10
+ } state[10];
+
+ void save(Common::WriteStream *stream, int16 *conversationDataBase);
+ void load(Common::ReadStream *stream, int16 *conversationDataBase);
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
new file mode 100644
index 0000000000..a0017f2571
--- /dev/null
+++ b/engines/toon/detection.cpp
@@ -0,0 +1,268 @@
+/* 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 "engines/advancedDetector.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "base/plugins.h"
+#include "graphics/thumbnail.h"
+#include "toon/toon.h"
+
+static const PlainGameDescriptor ToonGames[] = {
+ { "toon", "Toonstruck" },
+ { 0, 0 }
+};
+
+namespace Toon {
+
+using Common::GUIO_NONE;
+
+static const ADGameDescription gameDescriptions[] = {
+ {
+ "toon", "",
+ {
+ {"local.pak", 0, "3290209ef9bc92692108dd2f45df0736", 3237611},
+ {"arcaddbl.svl", 0, "c418478cd2833c7c983799f948af41ac", 7844688},
+ {"study.svl", 0, "281efa3f33f6712c0f641a605f4d40fd", 2511090},
+ AD_LISTEND
+ },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "toon", "",
+ {
+ {"local.pak", 0, "517132c3575b38806d1e7b6f59848072", 3224044},
+ {"arcaddbl.svl", 0, "ff74008827b62fbef1f46f104c438e44", 9699256},
+ {"study.svl", 0, "df056b94ea83f1ed92a539cf636053ab", 2542668},
+ AD_LISTEND
+ },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "toon", "",
+ {
+ {"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841},
+ {"arcaddbl.svl", 0, "7a0d74f4d66d1c722b946abbeb0834ef", 9122249},
+ {"study.svl", 0, "72fe96a9e10967d3138e918295babc42", 2910283},
+ AD_LISTEND
+ },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "toon", "",
+ {
+ {"local.pak", 0, "e8645168a247e2abdbfc2f9fa9d1c0fa", 3232222},
+ {"arcaddbl.svl", 0, "7893ac4cc78d51356baa058bbee7aa28", 8275016},
+ {"study.svl", 0, "b6b1ee2d9d94d53d305856039ab7bde7", 2634620},
+ AD_LISTEND
+ },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_NO_FLAGS, GUIO_NONE
+ },
+ {
+ "toon", "",
+ {
+ {"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841},
+ {"wacexdbl.emc", 0, "cfbc2156a31b294b038204888407ebc8", 6974},
+ {"generic.svl", 0, "5eb99850ada22f0b8cf6392262d4dd07", 9404599},
+ AD_LISTEND
+ },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_DEMO, GUIO_NONE
+ },
+
+ AD_TABLE_END_MARKER
+};
+
+static const ADFileBasedFallback fileBasedFallback[] = {
+ { &gameDescriptions[0], { "local.pak", "arcaddbl.svl", "study.svl", 0 } }, // default to english version
+ { 0, { 0 } }
+};
+
+} // End of namespace Toon
+
+static const char * const directoryGlobs[] = {
+ "misc",
+ "act1",
+ "arcaddbl",
+ "act2",
+ "study",
+ 0
+};
+
+static const ADParams detectionParams = {
+ (const byte *)Toon::gameDescriptions,
+ sizeof(ADGameDescription),
+ 5000, // number of md5 bytes
+ ToonGames,
+ 0, // no obsolete targets data
+ "toon",
+ Toon::fileBasedFallback,
+ 0,
+ // Additional GUI options (for every game}
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 3,
+ // List of directory globs
+ directoryGlobs
+};
+
+class ToonMetaEngine : public AdvancedMetaEngine {
+public:
+ ToonMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ virtual const char *getName() const {
+ return "Toon Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Toonstruck (C) 1996 Virgin Interactive";
+ }
+
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual SaveStateList listSaves(const char *target) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+// virtual void removeSaveState(const char *target, int slot) const;
+};
+
+bool ToonMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+// (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate);
+}
+
+int ToonMetaEngine::getMaximumSaveSlot() const { return 99; }
+
+SaveStateList ToonMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".???";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ int slotNum = 0;
+ for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ slotNum = atoi(filename->c_str() + filename->size() - 3);
+
+ if (slotNum >= 0 && slotNum <= 99) {
+ Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
+ if (file) {
+ int32 version = file->readSint32BE();
+ if (version != TOON_SAVEGAME_VERSION) {
+ delete file;
+ continue;
+ }
+
+ // read name
+ uint16 nameSize = file->readUint16BE();
+ if (nameSize >= 255) {
+ delete file;
+ continue;
+ }
+ char name[256];
+ file->read(name, nameSize);
+ name[nameSize] = 0;
+
+ saveList.push_back(SaveStateDescriptor(slotNum, name));
+ delete file;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+ Common::String fileName = Common::String::printf("%s.%03d", target, slot);
+ Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (file) {
+
+ int32 version = file->readSint32BE();
+ if (version != TOON_SAVEGAME_VERSION) {
+ delete file;
+ return SaveStateDescriptor();
+ }
+
+ uint32 saveNameLength = file->readUint16BE();
+ char saveName[256];
+ file->read(saveName, saveNameLength);
+ saveName[saveNameLength] = 0;
+
+ SaveStateDescriptor desc(slot, saveName);
+
+ Graphics::Surface *thumbnail = new Graphics::Surface();
+ assert(thumbnail);
+ if (!Graphics::loadThumbnail(*file, *thumbnail)) {
+ delete thumbnail;
+ thumbnail = 0;
+ }
+ desc.setThumbnail(thumbnail);
+
+ desc.setDeletableFlag(true);
+ desc.setWriteProtectedFlag(false);
+
+ uint32 saveDate = file->readUint32BE();
+ uint16 saveTime = file->readUint16BE();
+
+ int day = (saveDate >> 24) & 0xFF;
+ int month = (saveDate >> 16) & 0xFF;
+ int year = saveDate & 0xFFFF;
+
+ desc.setSaveDate(year, month, day);
+
+ int hour = (saveTime >> 8) & 0xFF;
+ int minutes = saveTime & 0xFF;
+
+ desc.setSaveTime(hour, minutes);
+
+ delete file;
+ return desc;
+ }
+
+ return SaveStateDescriptor();
+}
+
+bool ToonMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new Toon::ToonEngine(syst, desc);
+ }
+ return desc != 0;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(TOON)
+ REGISTER_PLUGIN_DYNAMIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine);
+#endif
diff --git a/engines/toon/drew.cpp b/engines/toon/drew.cpp
new file mode 100644
index 0000000000..b50a8db3dc
--- /dev/null
+++ b/engines/toon/drew.cpp
@@ -0,0 +1,122 @@
+/* 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 "toon/drew.h"
+
+namespace Toon {
+
+CharacterDrew::CharacterDrew(ToonEngine *vm) : Character(vm) {
+ _id = 0;
+ _blockingWalk = true;
+ _animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
+ _animationInstance->setUseMask(true);
+ vm->getAnimationManager()->addInstance(_animationInstance);
+}
+
+CharacterDrew::~CharacterDrew(void) {
+}
+
+bool CharacterDrew::setupPalette() {
+ debugC(1, kDebugCharacter, "setupPalette()");
+
+ if (_walkAnim) {
+ _walkAnim->applyPalette(129, 129 * 3, 63);
+ return true;
+ }
+ return false;
+}
+
+void CharacterDrew::setFacing(int32 facing) {
+ debugC(4, kDebugCharacter, "setFacing(%d)", facing);
+ _facing = facing;
+}
+
+void CharacterDrew::setPosition(int32 x, int32 y) {
+ debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
+
+ _z = _vm->getLayerAtPoint(x, y);
+ _scale = _vm->getScaleAtPoint(x, y);
+
+ // work out position and scale of the character sprite
+ int32 width = _walkAnim->getWidth() * _scale / 1024;
+ int32 height = 210 * _scale / 1024;
+ _animationInstance->setPosition(x - width / 2, y - height, _z , false);
+ _animationInstance->setScale(_scale);
+
+ // work out position and scale of the shadow below character
+ int32 shadowWidth = _shadowAnim->getWidth() * _scale / 1024;
+ int32 shadowHeight = _shadowAnim->getHeight() * _scale / 1024;
+
+ _shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 - 4 , _z , false);
+ _shadowAnimationInstance->setScale(_scale);
+
+ _x = x;
+ _y = y;
+ _animationInstance->setLayerZ(_y);
+}
+
+void CharacterDrew::playStandingAnim() {
+ debugC(4, kDebugCharacter, "playStandingAnim()");
+
+ stopSpecialAnim();
+ _animationInstance->setAnimation(_walkAnim);
+ _animationInstance->setFrame(_facing * 2);
+ _shadowAnimationInstance->setFrame(_facing);
+ _animationInstance->setAnimationRange(_facing * 2, _facing * 2);
+ _animationInstance->stopAnimation();
+ _animationInstance->setLooping(true);
+ //setVisible(true);
+
+}
+
+void CharacterDrew::playWalkAnim(int32 start, int32 end) {
+ debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end);
+
+ stopSpecialAnim();
+ _animationInstance->setAnimation(_walkAnim);
+ _shadowAnimationInstance->setFrame(_facing);
+ _animationInstance->setAnimationRange(16 + _facing * 14, 16 + _facing * 14 + 13);
+ _animationInstance->playAnimation();
+ _animationInstance->setFps(16);
+ _animationInstance->setLooping(true);
+
+ //setVisible(true);
+}
+
+void CharacterDrew::update(int32 timeIncrement) {
+ debugC(5, kDebugCharacter, "update(%d)", timeIncrement);
+ Character::update(timeIncrement);
+ setPosition(_x, _y);
+
+}
+
+int32 CharacterDrew::getRandomIdleAnim() {
+ debugC(3, kDebugCharacter, "getRandomIdleAnim()");
+
+ static const int32 idle[] = { 6, 9, 10, 11, 12 };
+ return idle[_vm->randRange(0, 4)];
+}
+} // End of namespace Toon
+
diff --git a/engines/toon/drew.h b/engines/toon/drew.h
new file mode 100644
index 0000000000..a5be4c76c3
--- /dev/null
+++ b/engines/toon/drew.h
@@ -0,0 +1,51 @@
+/* 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 TOON_DREW_H
+#define TOON_DREW_H
+
+#include "toon/character.h"
+
+
+namespace Toon {
+
+class ToonEngine;
+
+class CharacterDrew : public Character {
+public:
+ CharacterDrew(ToonEngine *vm);
+ virtual ~CharacterDrew(void);
+ bool setupPalette();
+ void setFacing(int32 facing);
+ void playStandingAnim();
+ void setPosition(int32 x, int32 y);
+ void update(int32 timeIncrement);
+ void playWalkAnim(int32 start, int32 end);
+ int32 getRandomIdleAnim();
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/flux.cpp b/engines/toon/flux.cpp
new file mode 100644
index 0000000000..9dd38cd37a
--- /dev/null
+++ b/engines/toon/flux.cpp
@@ -0,0 +1,140 @@
+/* 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 "toon/flux.h"
+
+namespace Toon {
+
+CharacterFlux::CharacterFlux(ToonEngine *vm) : Character(vm) {
+ _id = 1;
+ _animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
+ _animationInstance->setUseMask(true);
+ vm->getAnimationManager()->addInstance(_animationInstance);
+}
+
+CharacterFlux::~CharacterFlux(void) {
+}
+
+void CharacterFlux::playStandingAnim() {
+ debugC(4, kDebugCharacter, "playStandingAnim()");
+
+ _animationInstance->setAnimation(_walkAnim);
+ _animationInstance->setFrame(_facing * 3);
+ _animationInstance->setAnimationRange(_facing * 3, _facing * 3);
+ _animationInstance->stopAnimation();
+ _animationInstance->setLooping(true);
+
+ //s/etVisible(true);
+}
+
+void CharacterFlux::setVisible(bool visible) {
+ if (_vm->state()->_currentChapter == 2) {
+ Character::setVisible(false);
+ } else {
+ Character::setVisible(visible);
+ }
+}
+
+void CharacterFlux::playWalkAnim(int32 start, int32 end) {
+ debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end);
+
+ _animationInstance->setAnimation(_walkAnim);
+ _animationInstance->setAnimationRange(24 + _facing * 10, 24 + _facing * 10 + 9);
+ _animationInstance->playAnimation();
+ _animationInstance->setFps(16);
+ _animationInstance->setLooping(true);
+}
+
+int32 CharacterFlux::fixFacingForAnimation(int32 originalFacing, int32 animationId) {
+
+ static const byte fixFluxAnimationFacing[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ byte animFacingFlag = fixFluxAnimationFacing[animationId];
+ int32 v5 = 1 << originalFacing;
+ int32 v6 = 1 << originalFacing;
+ int32 facingMask = 0;
+ do {
+ if (v6 & animFacingFlag) {
+ facingMask = v6;
+ } else if (v5 & animFacingFlag) {
+ facingMask = v5;
+ }
+ v5 >>= 1;
+ v6 <<= 1;
+ }
+ while (!facingMask);
+
+ int32 finalFacing = 0;
+ for (finalFacing = 0; ; ++finalFacing) {
+ facingMask >>= 1;
+ if (!facingMask)
+ break;
+ }
+
+ return finalFacing;
+}
+
+void CharacterFlux::setPosition(int32 x, int32 y) {
+ debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
+
+ _z = _vm->getLayerAtPoint(x, y);
+ _scale = _vm->getScaleAtPoint(x, y);
+ int32 width = _walkAnim->getWidth() * _scale / 1024;
+ int32 height = 165 * _scale / 1024;
+ _animationInstance->setPosition(x - width / 2, y - height, _z , false);
+ _animationInstance->setScale(_scale);
+
+ // in original code, flux shadow scale is 3/4 of real scale
+ int32 shadowScale = _scale * 3 / 4;
+
+ // work out position and scale of the shadow below character
+ int32 shadowWidth = _shadowAnim->getWidth() * shadowScale / 1024;
+ int32 shadowHeight = _shadowAnim->getHeight() * shadowScale / 1024;
+ _shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 , _z , false);
+ _shadowAnimationInstance->setScale(shadowScale);
+ _x = x;
+ _y = y;
+ _finalX = x;
+ _finalY = y;
+ _animationInstance->setLayerZ(_y);
+}
+
+void CharacterFlux::update(int32 timeIncrement) {
+ debugC(5, kDebugCharacter, "update(%d)", timeIncrement);
+ Character::update(timeIncrement);
+ setPosition(_x, _y);
+}
+
+int32 CharacterFlux::getRandomIdleAnim() {
+ debugC(3, kDebugCharacter, "getRandomIdleAnim()");
+ static const int32 idle[] = { 0xe, 0xf, 0x21, 0x22, 0x24, 0x25, 0x27 };
+ return idle[_vm->randRange(0, 6)];
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/flux.h b/engines/toon/flux.h
new file mode 100644
index 0000000000..7981799cba
--- /dev/null
+++ b/engines/toon/flux.h
@@ -0,0 +1,52 @@
+/* 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 TOON_FLUX_H
+#define TOON_FLUX_H
+
+
+#include "toon/character.h"
+
+class ToonEngine;
+
+namespace Toon {
+
+class CharacterFlux : public Character {
+public:
+ CharacterFlux(ToonEngine *vm);
+ virtual ~CharacterFlux(void);
+
+ void setPosition(int32 x, int32 y);
+ void playStandingAnim();
+ void playWalkAnim(int32 start, int32 end);
+ void update(int32 timeIncrement);
+ int32 getRandomIdleAnim();
+ void setVisible(bool visible);
+ static int32 fixFacingForAnimation(int32 originalFacing, int32 animationId);
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/font.cpp b/engines/toon/font.cpp
new file mode 100644
index 0000000000..be5a306d8c
--- /dev/null
+++ b/engines/toon/font.cpp
@@ -0,0 +1,274 @@
+/* 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 "toon/font.h"
+
+namespace Toon {
+
+FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) {
+ _currentFontColor[0] = 0;
+ _currentFontColor[1] = 0xc8;
+ _currentFontColor[2] = 0xcb;
+ _currentFontColor[3] = 0xce;
+
+}
+
+// mapping extended characters required for foreign versions to font (animation)
+static const byte map_textToFont[0x80] = {
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xBx
+ '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx
+ '?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', '?', 0x1f, '?', '?', 0x19, // 0xDx
+ 0x0d, 0x04, 0x0e, '?', 0x1a, '?', '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx
+ 0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx
+};
+
+void FontRenderer::renderText(int32 x, int32 y, Common::String origText, int32 mode) {
+ debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
+
+ int32 xx, yy;
+ computeSize(origText, &xx, &yy);
+
+ if (mode & 2) {
+ y -= yy / 2;
+ } else if (mode & 4) {
+ y -= yy;
+ }
+
+ if (mode & 1) {
+ x -= xx / 2;
+ }
+
+ int32 curX = x;
+ int32 curY = y;
+ int32 height = 0;
+
+ const byte *text = (const byte *)origText.c_str();
+ while (*text) {
+ byte curChar = *text;
+ if (curChar == 13) {
+ curY = curY + height;
+ height = 0;
+ curX = x;
+ } else {
+ if (curChar >= 0x80)
+ curChar = map_textToFont[curChar - 0x80];
+ _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
+ curX = curX + _currentFont->getFrameWidth(curChar) - 1;
+ height = MAX(height, _currentFont->getFrameHeight(curChar));
+ }
+ text++;
+ }
+}
+
+void FontRenderer::computeSize(Common::String origText, int32 *retX, int32 *retY) {
+ debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str());
+
+ int32 lineWidth = 0;
+ int32 lineHeight = 0;
+ int32 totalHeight = 0;
+ int32 totalWidth = 0;
+
+ const byte *text = (const byte *)origText.c_str();
+ while (*text) {
+ byte curChar = *text;
+ if (curChar < 32) {
+ text++;
+ continue;
+ } else if (curChar == 13) {
+ totalWidth = MAX(totalWidth, lineWidth);
+ totalHeight += lineHeight;
+ lineHeight = 0;
+ lineWidth = 0;
+ } else {
+ if (curChar >= 0x80)
+ curChar = map_textToFont[curChar - 0x80];
+ int32 charWidth = _currentFont->getFrameWidth(curChar) - 1;
+ int32 charHeight = _currentFont->getFrameHeight(curChar);
+ lineWidth += charWidth;
+ lineHeight = MAX(lineHeight, charHeight);
+ }
+ text++;
+ }
+
+ totalHeight += lineHeight;
+ totalWidth = MAX(totalWidth, lineWidth);
+
+ *retX = totalWidth;
+ *retY = totalHeight;
+}
+
+void FontRenderer::setFont(Animation *font) {
+ debugC(5, kDebugFont, "setFont(font)");
+
+ _currentFont = font;
+}
+
+void FontRenderer::setFontColorByCharacter(int32 characterId) {
+ debugC(5, kDebugFont, "setFontColorByCharacter(%d)", characterId);
+
+ // unfortunately this table was hardcoded in the original executable
+ static const byte colorsByCharacters[] = {
+ 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xe9, 0xde, 0xc8, 0xeb, 0xe8, 0xc8,
+ 0xd1, 0xcf, 0xc8, 0xd8, 0xd5, 0xc8, 0xfb, 0xfa, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xe8, 0xe4, 0xc8, 0xe9, 0xfa, 0xc8, 0xeb, 0xe4, 0xc8, 0xeb, 0xe4, 0xc8,
+ 0xd2, 0xea, 0xc8, 0xd3, 0xd0, 0xc8, 0xe1, 0xdd, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd2, 0xcf, 0xc8, 0xd1, 0xcf, 0xc8, 0xd9, 0xd7, 0xc8, 0xe3, 0xdd, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xe6, 0xe4, 0xc8, 0xd9, 0xd7, 0xc8, 0xcd, 0xca, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xe8, 0xe8, 0xc8, 0xdb, 0xd5, 0xc8,
+ 0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xd3, 0xd0, 0xc8, 0xd1, 0xcf, 0xc8,
+ 0xe6, 0xe4, 0xc8, 0xd1, 0xcf, 0xc8, 0xd2, 0xcf, 0xc8, 0xcc, 0xcb, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
+ 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8
+ };
+
+ setFontColor(colorsByCharacters[characterId * 3 + 2], colorsByCharacters[characterId * 3 + 1], colorsByCharacters[characterId * 3 + 0]);
+}
+
+void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3) {
+ debugC(5, kDebugFont, "setFontColor(%d, %d, %d)", fontColor1, fontColor2, fontColor3);
+
+ _currentFontColor[0] = 0;
+ _currentFontColor[1] = fontColor1;
+ _currentFontColor[2] = fontColor2;
+ _currentFontColor[3] = fontColor3;
+}
+
+void FontRenderer::renderMultiLineText(int32 x, int32 y, Common::String origText, int32 mode) {
+ debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
+
+ // divide the text in several lines
+ // based on number of characters or size of lines.
+ byte text[1024];
+ strncpy((char *)text, origText.c_str(), 1023);
+ text[1023] = 0;
+
+ byte *lines[16];
+ int32 lineSize[16];
+ int32 numLines = 0;
+
+ byte *it = text;
+
+ int32 maxWidth = 0;
+ int32 curWidth = 0;
+
+ while (1) {
+ byte *lastLine = it;
+ byte *lastSpace = it;
+ int32 lastSpaceX = 0;
+ int32 curLetterNr = 0;
+ curWidth = 0;
+
+ while (*it && curLetterNr < 50 && curWidth < 580) {
+ byte curChar = *it;
+ if (curChar == 32) {
+ lastSpace = it;
+ lastSpaceX = curWidth;
+ } else if (curChar >= 0x80) {
+ curChar = map_textToFont[curChar - 0x80];
+ }
+
+ int width = _currentFont->getFrameWidth(curChar);
+ curWidth += width - 2;
+ it++;
+ curLetterNr++;
+ }
+
+ if (*lastLine == 0)
+ break;
+
+ lines[numLines] = lastLine;
+
+ if (*it == 0)
+ lineSize[numLines] = curWidth;
+ else
+ lineSize[numLines] = lastSpaceX;
+
+ if (lineSize[numLines] > maxWidth)
+ maxWidth = lineSize[numLines];
+
+ lastLine = lastSpace + 1;
+ numLines++;
+
+ if (*it == 0)
+ break;
+
+ it = lastLine;
+ *lastSpace = 0;
+
+ if (numLines >= 16)
+ break;
+ }
+
+ if (curWidth > maxWidth) {
+ maxWidth = curWidth;
+ }
+ //numLines++;
+
+ // get font height (assumed to be constant)
+ int32 height = _currentFont->getHeight();
+ int textSize = (height - 2) * numLines;
+ y = y - textSize;
+ if (y < 30)
+ y = 30;
+ if (y + textSize > 370)
+ y = 370 - textSize;
+
+ x -= _vm->state()->_currentScrollValue;
+
+ // adapt x
+ if (x - 30 - maxWidth / 2 < 0)
+ x = maxWidth / 2 + 30;
+
+ if (x + 30 + (maxWidth / 2) > 640)
+ x = 640 - (maxWidth / 2) - 30;
+
+ // we have good coordinates now, we can render the multi line
+ int32 curX = x;
+ int32 curY = y;
+
+ for (int32 i = 0; i < numLines; i++) {
+ const byte *line = lines[i];
+ curX = x - lineSize[i] / 2;
+ while (*line) {
+ byte curChar = *line;
+ if (curChar >= 0x80)
+ curChar = map_textToFont[curChar - 0x80];
+ if (curChar != 32) _currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor);
+ curX = curX + _currentFont->getFrameWidth(curChar) - 2;
+ //height = MAX(height, _currentFont->getFrameHeight(curChar));
+ line++;
+ }
+ curY += height;
+ }
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/font.h b/engines/toon/font.h
new file mode 100644
index 0000000000..713d8c3409
--- /dev/null
+++ b/engines/toon/font.h
@@ -0,0 +1,52 @@
+/* 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 TOON_FONT_H
+#define TOON_FONT_H
+
+#include "toon/toon.h"
+
+namespace Toon {
+
+class FontRenderer {
+public:
+ FontRenderer(ToonEngine *vm);
+ ~FontRenderer(void);
+
+ void setFont(Animation *font);
+ void computeSize(Common::String origText, int32 *retX, int32 *retY);
+ void renderText(int32 x, int32 y, Common::String origText, int32 mode);
+ void renderMultiLineText(int32 x, int32 y, Common::String origText, int32 mode);
+ void setFontColorByCharacter(int32 characterId);
+ void setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3);
+protected:
+ Animation *_currentFont;
+ ToonEngine *_vm;
+ byte _currentFontColor[4];
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/hotspot.cpp b/engines/toon/hotspot.cpp
new file mode 100644
index 0000000000..5af61197d7
--- /dev/null
+++ b/engines/toon/hotspot.cpp
@@ -0,0 +1,157 @@
+/* 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 "toon/hotspot.h"
+#include "toon/tools.h"
+
+namespace Toon {
+
+Hotspots::Hotspots(ToonEngine *vm) : _vm(vm) {
+ _items = 0;
+ _numItems = 0;
+}
+
+
+void Hotspots::load(Common::ReadStream *Stream) {
+ delete[] _items;
+
+ _numItems = Stream->readSint16BE();
+ _items = new HotspotData[_numItems];
+
+ for (int32 i = 0; i < _numItems; i++) {
+ for (int32 a = 0; a < 256; a++)
+ _items[i].setData(a, Stream->readSint16BE());
+ }
+}
+
+void Hotspots::save(Common::WriteStream *Stream) {
+
+ Stream->writeSint16BE(_numItems);
+
+ for (int32 i = 0; i < _numItems; i++) {
+ for (int32 a = 0; a < 256; a++)
+ Stream->writeSint16BE(_items[i].getData(a));
+ }
+}
+
+int32 Hotspots::FindBasedOnCorner(int32 x, int32 y) {
+ debugC(1, kDebugHotspot, "FindBasedOnCorner(%d, %d)", x, y);
+
+ for (int32 i = 0; i < _numItems; i++) {
+ if (x == _items[i].getX1()) {
+ if (y == _items[i].getY1()) {
+ if (_items[i].getMode() == -1)
+ return _items[i].getRef();
+
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+int32 Hotspots::Find(int32 x, int32 y) {
+ debugC(6, kDebugHotspot, "Find(%d, %d)", x, y);
+
+ int32 priority = -1;
+// Strangerke - Commented (not used)
+// bool found = false;
+ int32 foundId = -1;
+ int32 testId = -1;
+
+ for (int i = 0; i < _numItems; i++) {
+ if (x >= _items[i].getX1() && x <= _items[i].getX2() && y >= _items[i].getY1() && y <= _items[i].getY2()) {
+ if (_items[i].getMode() == -1)
+ testId = _items[i].getRef();
+ else
+ testId = i;
+
+ if (_items[testId].getPriority() > priority) {
+// Strangerke - Commented (not used)
+// found = true;
+ foundId = testId;
+ priority = _items[testId].getPriority();
+ }
+ }
+ }
+ return foundId;
+}
+
+bool Hotspots::LoadRif(Common::String rifName, Common::String additionalRifName) {
+ debugC(1, kDebugHotspot, "LoadRif(%s, %s)", rifName.c_str(), additionalRifName.c_str());
+
+ uint32 size = 0;
+ uint8 *rifData = _vm->resources()->getFileData(rifName, &size);
+ if (!rifData)
+ return false;
+
+ uint32 size2 = 0;
+ uint8 *rifData2 = 0;
+ if (additionalRifName.size())
+ rifData2 = _vm->resources()->getFileData(additionalRifName, &size2);
+
+ // figure out the number of hotspots based on file size
+ int32 rifsize = READ_BE_UINT32(&rifData[4]);
+ int32 rifsize2 = 0;
+
+ if (size2)
+ rifsize2 = READ_BE_UINT32(&rifData2[4]);
+
+ _numItems = (rifsize + rifsize2) / 512;
+
+ if (_items)
+ delete[] _items;
+
+ _items = new HotspotData[_numItems];
+
+ // RIFs are compressed in RNC1
+ RncDecoder decoder;
+ decoder.unpackM1(rifData, _items);
+ if (rifsize2) {
+ RncDecoder decoder2;
+ decoder2.unpackM1(rifData2 , _items + (rifsize >> 9));
+ for (int32 i = 0; i < (rifsize2 >> 9); i++) {
+ HotspotData *hot = _items + (rifsize >> 9) + i;
+ hot->setData(0, hot->getX1() + 1280);
+ hot->setData(2, hot->getX2() + 1280);
+ if (hot->getMode() == -1)
+ hot->setData(5, hot->getRef() + (rifsize >> 9));
+ }
+ }
+
+ return true;
+}
+
+HotspotData *Hotspots::Get(int32 id) {
+ debugC(5, kDebugHotspot, "Get(%d)", id);
+
+ if (id < 0 || id >= _numItems)
+ return 0;
+ else
+ return &_items[id];
+}
+
+} // End of namespace Toon
+
diff --git a/engines/toon/hotspot.h b/engines/toon/hotspot.h
new file mode 100644
index 0000000000..233bcebcb7
--- /dev/null
+++ b/engines/toon/hotspot.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 TOON_HOTSPOT_H
+#define TOON_HOTSPOT_H
+
+#include "toon/toon.h"
+#include "toon/tools.h"
+
+namespace Toon {
+
+class HotspotData {
+public:
+ int16 getX1() const { return READ_LE_INT16(_data + 0); }
+ int16 getY1() const { return READ_LE_INT16(_data + 1); }
+ int16 getX2() const { return READ_LE_INT16(_data + 2); }
+ int16 getY2() const { return READ_LE_INT16(_data + 3); }
+ int16 getMode() const { return READ_LE_INT16(_data + 4); }
+ int16 getRef() const { return READ_LE_INT16(_data + 5); }
+ int16 getPriority() const { return READ_LE_INT16(_data + 7); }
+ int16 getType() const { return READ_LE_INT16(_data + 8); }
+ int16 getData(int32 id) const { return READ_LE_INT16(_data + id); }
+ void setData(int32 id, int16 val) { WRITE_LE_UINT16(&_data[id], val); }
+
+private:
+ int16 _data[256];
+};
+
+class Hotspots {
+public:
+ Hotspots(ToonEngine *vm);
+ ~Hotspots(void);
+
+ bool LoadRif(Common::String rifName, Common::String additionalRifName);
+ int32 Find(int32 x, int32 y);
+ int32 FindBasedOnCorner(int32 x, int32 y);
+ HotspotData *Get(int32 id);
+ int32 getCount() const { return _numItems; }
+
+ void load(Common::ReadStream *Stream);
+ void save(Common::WriteStream *Stream);
+
+protected:
+ HotspotData *_items;
+ int32 _numItems;
+ ToonEngine *_vm;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/module.mk b/engines/toon/module.mk
new file mode 100644
index 0000000000..403408e497
--- /dev/null
+++ b/engines/toon/module.mk
@@ -0,0 +1,30 @@
+MODULE := engines/toon
+
+MODULE_OBJS := \
+ anim.o \
+ audio.o \
+ character.o \
+ conversation.o \
+ detection.o \
+ drew.o \
+ flux.o \
+ font.o \
+ hotspot.o \
+ movie.o \
+ path.o \
+ picture.o \
+ resource.o \
+ script.o \
+ script_func.o \
+ state.o \
+ text.o \
+ tools.o \
+ toon.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_TOON), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp
new file mode 100644
index 0000000000..87a3e878b5
--- /dev/null
+++ b/engines/toon/movie.cpp
@@ -0,0 +1,111 @@
+/* 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 "toon/movie.h"
+
+namespace Toon {
+
+void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
+ debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize);
+
+ if (track == 1 && chunkSize == 4) {
+ /* uint16 width = */ _fileStream->readUint16LE();
+ uint16 height = _fileStream->readUint16LE();
+
+ _header.flags = (height == getHeight() / 2) ? 4 : 0;
+ } else
+ Graphics::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize);
+}
+
+bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename, int forcedflags) {
+ debugC(1, kDebugMovie, "loadFile(%s, %d)", filename.c_str(), forcedflags);
+
+ if (Graphics::SmackerDecoder::loadFile(filename)) {
+ if (forcedflags & 0x10 || _surface->h == 200) {
+
+ _header.flags = 4;
+ delete this->_surface;
+ _surface = new Graphics::Surface();
+ _surface->create(640, 400, 1);
+ }
+ return true;
+ }
+ return false;
+}
+
+ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Graphics::SmackerDecoder(mixer, soundType) {
+
+}
+
+Movie::Movie(ToonEngine *vm , ToonstruckSmackerDecoder *decoder) {
+ _vm = vm;
+ _decoder = decoder;
+}
+
+Movie::~Movie() {
+}
+
+void Movie::init() const {
+}
+
+void Movie::play(Common::String video, int32 flags) {
+ debugC(1, kDebugMovie, "play(%s, %d)", video.c_str(), flags);
+
+ if (flags & 1)
+ _vm->getAudioManager()->setMusicVolume(0);
+ _decoder->loadFile(video.c_str(), flags);
+ playVideo();
+ _vm->flushPalette();
+ if (flags & 1)
+ _vm->getAudioManager()->setMusicVolume(100);
+ _decoder->close();
+}
+
+bool Movie::playVideo() {
+ debugC(1, kDebugMovie, "playVideo()");
+
+ int32 x = 0;
+ int32 y = 0;
+ while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
+ if (_decoder->needsUpdate()) {
+ Graphics::Surface *frame = _decoder->decodeNextFrame();
+ if (frame)
+ _vm->getSystem()->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+ _decoder->setSystemPalette();
+ _vm->getSystem()->updateScreen();
+ }
+
+ Common::Event event;
+ while (_vm->getSystem()->getEventManager()->pollEvent(event))
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) {
+ return false;
+ }
+
+ _vm->getSystem()->delayMillis(10);
+ }
+ return !_vm->shouldQuit();
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/movie.h b/engines/toon/movie.h
new file mode 100644
index 0000000000..8e1acc4a77
--- /dev/null
+++ b/engines/toon/movie.h
@@ -0,0 +1,58 @@
+/* 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 TOON_MOVIE_H
+#define TOON_MOVIE_H
+
+#include "toon/toon.h"
+#include "graphics/video/smk_decoder.h"
+
+namespace Toon {
+
+class ToonstruckSmackerDecoder : public Graphics::SmackerDecoder {
+public:
+ ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
+ void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize);
+ bool loadFile(const Common::String &filename, int forcedflags) ;
+};
+
+class Movie {
+public:
+ Movie(ToonEngine *vm, ToonstruckSmackerDecoder *decoder);
+ ~Movie(void);
+
+ void init() const;
+ void play(Common::String video, int32 flags = 0);
+
+protected:
+ bool playVideo();
+ ToonEngine *_vm;
+ Audio::Mixer *_mixer;
+ ToonstruckSmackerDecoder *_decoder;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/path.cpp b/engines/toon/path.cpp
new file mode 100644
index 0000000000..484e621a64
--- /dev/null
+++ b/engines/toon/path.cpp
@@ -0,0 +1,370 @@
+/* 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 "toon/path.h"
+
+namespace Toon {
+
+int32 PathFindingHeap::init(int32 size) {
+ debugC(1, kDebugPath, "init(%d)", size);
+
+ _data = new HeapDataGrid[size * 2];
+ memset(_data, 0, sizeof(HeapDataGrid) * size * 2);
+ _count = 0;
+ _alloc = size;
+ return size;
+}
+
+int PathFindingHeap::unload() {
+ if (_data)
+ delete[] _data;
+ return 0;
+}
+
+int PathFindingHeap::clear() {
+ //debugC(1, kDebugPath, "clear()");
+
+ _count = 0;
+ memset(_data, 0, sizeof(HeapDataGrid) * _alloc * 2);
+ return 1;
+}
+
+int PathFindingHeap::push(int x, int y, int weight) {
+ //debugC(6, kDebugPath, "push(%d, %d, %d)", x, y, weight);
+
+ _count++;
+ _data[_count]._x = x;
+ _data[_count]._y = y;
+ _data[_count]._weight = weight;
+
+ int32 lMax = _count;
+ int32 lT = 0;
+
+ while (1) {
+ lT = lMax / 2;
+ if (lT < 1)
+ break;
+
+ if (_data[lT]._weight > _data[lMax]._weight) {
+ HeapDataGrid temp;
+ temp = _data[lT];
+ _data[lT] = _data[lMax];
+ _data[lMax] = temp;
+ lMax = lT;
+ } else {
+ break;
+ }
+ }
+ return 1;
+}
+
+int32 PathFindingHeap::pop(int32 *x, int32 *y, int32 *weight) {
+ //debugC(6, kDebugPath, "pop(x, y, weight)");
+
+ if (!_count)
+ return 0;
+
+ *x = _data[1]._x;
+ *y = _data[1]._y;
+ *weight = _data[1]._weight;
+
+ _data[1] = _data[_count];
+ _count--;
+ if (!_count)
+ return 0;
+
+ int32 lMin = 1;
+ int32 lT = 1;
+
+ while (1) {
+ lT = lMin << 1;
+ if (lT <= _count) {
+ if (lT < _count) {
+ if (_data[lT + 1]._weight < _data[lT]._weight)
+ lT++;
+ }
+ if (_data[lT]._weight <= _data[lMin]._weight) {
+ HeapDataGrid temp;
+ temp = _data[lMin];
+ _data[lMin] = _data[lT];
+ _data[lT] = temp;
+
+ lMin = lT;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
+
+PathFinding::PathFinding(ToonEngine *vm) : _vm(vm) {
+ _width = 0;
+ _height = 0;
+ _heap = new PathFindingHeap();
+ _gridTemp = 0;
+ _numBlockingRects = 0;
+}
+
+PathFinding::~PathFinding(void) {
+ if (_heap) {
+ _heap->unload();
+ delete _heap;
+ }
+}
+
+bool PathFinding::isWalkable(int32 x, int32 y) {
+ //debugC(6, kDebugPath, "isWalkable(%d, %d)", x, y);
+
+ bool maskWalk = (_currentMask->getData(x, y) & 0x1f) > 0;
+ for (int32 i = 0; i < _numBlockingRects; i++) {
+ if (_blockingRects[i][4] == 0) {
+ if (x >= _blockingRects[i][0] && x <= _blockingRects[i][2] && y >= _blockingRects[i][1] && y < _blockingRects[i][3])
+ return false;
+ } else {
+ int32 dx = abs(_blockingRects[i][0] - x);
+ int32 dy = abs(_blockingRects[i][1] - y);
+ if ((dx << 8) / _blockingRects[i][2] < (1 << 8) && (dy << 8) / _blockingRects[i][3] < (1 << 8)) {
+ return false;
+ }
+ }
+ }
+ return maskWalk;
+}
+
+int32 PathFinding::findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 *fyy, int origX, int origY) {
+ debugC(1, kDebugPath, "findClosestWalkingPoint(%d, %d, fxx, fyy, %d, %d)", xx, yy, origX, origY);
+
+ int32 currentFound = -1;
+ int32 dist = -1;
+ int32 dist2 = -1;
+
+ if (origX == -1)
+ origX = xx;
+ if (origY == -1)
+ origY = yy;
+
+ for (int y = 0; y < _height; y++) {
+ for (int x = 0; x < _width; x++) {
+ if (isWalkable(x, y)) {
+ int32 ndist = (x - xx) * (x - xx) + (y - yy) * (y - yy);
+ int32 ndist2 = (x - origX) * (x - origX) + (y - origY) * (y - origY);
+ if (currentFound < 0 || ndist < dist || (ndist == dist && ndist2 < dist2)) {
+ dist = ndist;
+ dist2 = ndist2;
+ currentFound = y * _width + x;
+ }
+ }
+ }
+ }
+
+ if (currentFound != -1) {
+ *fxx = currentFound % _width;
+ *fyy = currentFound / _width;
+ return 1;
+ } else {
+ *fxx = 0;
+ *fyy = 0;
+ return 0;
+ }
+}
+
+int PathFinding::findPath(int32 x, int32 y, int32 destx, int32 desty) {
+ debugC(1, kDebugPath, "findPath(%d, %d, %d, %d)", x, y, destx, desty);
+
+ if (x == destx && y == desty) {
+ _gridPathCount = 0;
+ return true;
+ }
+
+ memset(_gridTemp , 0, _width * _height * sizeof(int32));
+ _heap->clear();
+ int32 curX = x;
+ int32 curY = y;
+ int32 curWeight = 0;
+ int32 *sq = _gridTemp;
+
+ sq[curX + curY *_width] = 1;
+ _heap->push(curX, curY, abs(destx - x) + abs(desty - y));
+ int wei = 0;
+
+// Strangerke - Commented (not used)
+// byte *mask = _currentMask->getDataPtr();
+
+ while (_heap->_count) {
+ wei = 0;
+ _heap->pop(&curX, &curY, &curWeight);
+ int curNode = curX + curY * _width;
+
+ int32 endX = MIN(curX + 1, _width - 1);
+ int32 endY = MIN(curY + 1, _height - 1);
+ int32 startX = MAX(curX - 1, 0);
+ int32 startY = MAX(curY - 1, 0);
+
+ for (int32 px = startX; px <= endX; px++) {
+ for (int py = startY; py <= endY; py++) {
+ if (px != curX || py != curY) {
+ wei = abs(px - curX) + abs(py - curY);
+
+ int32 curPNode = px + py * _width;
+ if (isWalkable(px, py)) { // walkable ?
+ int sum = sq[curNode] + wei;
+ if (sq[curPNode] > sum || !sq[curPNode]) {
+ int newWeight = abs(destx - px) + abs(desty - py);
+ sq[curPNode] = sum;
+ _heap->push(px, py, sq[curPNode] + newWeight);
+ if (!newWeight)
+ goto next; // we found it !
+ }
+ }
+ }
+ }
+ }
+ }
+
+next:
+
+ // let's see if we found a result !
+ if (!_gridTemp[destx + desty * _width]) {
+ // didn't find anything
+ _gridPathCount = 0;
+ return false;
+ }
+
+ curX = destx;
+ curY = desty;
+
+ int32 retPathX[4096];
+ int32 retPathY[4096];
+ int32 numpath = 0;
+
+ retPathX[numpath] = curX;
+ retPathY[numpath] = curY;
+ numpath++;
+ int32 bestscore = sq[destx + desty * _width];
+
+ while (1) {
+ int32 bestX = -1;
+ int32 bestY = -1;
+
+ int32 endX = MIN(curX + 1, _width - 1);
+ int32 endY = MIN(curY + 1, _height - 1);
+ int32 startX = MAX(curX - 1, 0);
+ int32 startY = MAX(curY - 1, 0);
+
+ for (int32 px = startX; px <= endX; px++) {
+ for (int32 py = startY; py <= endY; py++) {
+ if (px != curX || py != curY) {
+ wei = abs(px - curX) + abs(py - curY);
+
+ int PNode = px + py * _width;
+ if (sq[PNode] && (isWalkable(px, py))) {
+ if (sq[PNode] < bestscore) {
+ bestscore = sq[PNode];
+ bestX = px;
+ bestY = py;
+ }
+ }
+ }
+ }
+ }
+
+ if (bestX < 0 || bestY < 0)
+ return 0;
+
+ retPathX[numpath] = bestX;
+ retPathY[numpath] = bestY;
+ numpath++;
+
+ if ((bestX == x && bestY == y)) {
+ _gridPathCount = numpath;
+
+ memcpy(_tempPathX, retPathX, sizeof(int32) * numpath);
+ memcpy(_tempPathY, retPathY, sizeof(int32) * numpath);
+ return true;
+ }
+
+ curX = bestX;
+ curY = bestY;
+ }
+
+ return false;
+}
+
+void PathFinding::init(Picture *mask) {
+ debugC(1, kDebugPath, "init(mask)");
+
+ _width = mask->getWidth();
+ _height = mask->getHeight();
+ _currentMask = mask;
+ _heap->unload();
+ _heap->init(_width * _height);
+ if (_gridTemp)
+ delete[] _gridTemp;
+ _gridTemp = new int32[_width*_height];
+}
+
+void PathFinding::resetBlockingRects() {
+ _numBlockingRects = 0;
+}
+
+void PathFinding::addBlockingRect(int32 x1, int32 y1, int32 x2, int32 y2) {
+ debugC(1, kDebugPath, "addBlockingRect(%d, %d, %d, %d)", x1, y1, x2, y2);
+
+ _blockingRects[_numBlockingRects][0] = x1;
+ _blockingRects[_numBlockingRects][1] = y1;
+ _blockingRects[_numBlockingRects][2] = x2;
+ _blockingRects[_numBlockingRects][3] = y2;
+ _blockingRects[_numBlockingRects][4] = 0;
+ _numBlockingRects++;
+}
+
+void PathFinding::addBlockingEllipse(int32 x1, int32 y1, int32 w, int32 h) {
+ debugC(1, kDebugPath, "addBlockingRect(%d, %d, %d, %d)", x1, y1, w, h);
+
+ _blockingRects[_numBlockingRects][0] = x1;
+ _blockingRects[_numBlockingRects][1] = y1;
+ _blockingRects[_numBlockingRects][2] = w;
+ _blockingRects[_numBlockingRects][3] = h;
+ _blockingRects[_numBlockingRects][4] = 1;
+ _numBlockingRects++;
+}
+
+
+int32 PathFinding::getPathNodeCount() const {
+ return _gridPathCount;
+}
+
+int32 PathFinding::getPathNodeX(int32 nodeId) const {
+ return _tempPathX[ _gridPathCount - nodeId - 1];
+}
+
+int32 PathFinding::getPathNodeY(int32 nodeId) const {
+ return _tempPathY[ _gridPathCount - nodeId - 1];
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/path.h b/engines/toon/path.h
new file mode 100644
index 0000000000..d8ef2eac02
--- /dev/null
+++ b/engines/toon/path.h
@@ -0,0 +1,93 @@
+/* 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 TOON_PATH_H
+#define TOON_PATH_H
+
+#include "toon/toon.h"
+
+namespace Toon {
+
+// binary heap system for fast A*
+struct HeapDataGrid {
+ int16 _x, _y;
+ int16 _weight;
+};
+
+class PathFindingHeap {
+
+private:
+ HeapDataGrid *_data;
+public:
+ int32 _alloc;
+ int32 _count;
+ int32 push(int32 x, int32 y, int32 weight);
+ int32 pop(int32 *x, int32 *y, int32 *weight);
+ int32 init(int32 size);
+ int32 clear();
+ int32 unload();
+};
+
+
+class PathFinding {
+public:
+ PathFinding(ToonEngine *vm);
+ ~PathFinding(void);
+
+ int32 findPath(int32 x, int32 y, int32 destX, int32 destY);
+ int32 findClosestWalkingPoint(int32 xx, int32 yy, int32 *fxx, int32 *fyy, int origX = -1, int origY = -1);
+ bool isWalkable(int32 x, int32 y);
+ void init(Picture *mask);
+
+ void resetBlockingRects();
+ void addBlockingRect(int32 x1, int32 y1, int32 x2, int32 y2);
+ void addBlockingEllipse(int32 x1, int32 y1, int32 w, int32 h);
+
+ int32 getPathNodeCount() const;
+ int32 getPathNodeX(int32 nodeId) const;
+ int32 getPathNodeY(int32 nodeId) const;
+protected:
+ Picture *_currentMask;
+
+ PathFindingHeap *_heap;
+
+ int32 *_gridTemp;
+ int32 _width;
+ int32 _height;
+
+ int32 _tempPathX[4096];
+ int32 _tempPathY[4096];
+ int32 _blockingRects[16][5];
+ int32 _numBlockingRects;
+ int32 _allocatedGridPathCount;
+ int32 _gridPathCount;
+
+ ToonEngine *_vm;
+
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/picture.cpp b/engines/toon/picture.cpp
new file mode 100644
index 0000000000..11a5572066
--- /dev/null
+++ b/engines/toon/picture.cpp
@@ -0,0 +1,296 @@
+/* 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 "toon/picture.h"
+#include "toon/tools.h"
+#include "common/stack.h"
+
+namespace Toon {
+
+bool Picture::loadPicture(Common::String file, bool totalPalette /*= false*/) {
+ debugC(1, kDebugPicture, "loadPicture(%s, %d)", file.c_str(), (totalPalette) ? 1 : 0);
+
+ uint32 size = 0;
+ uint8 *fileData = _vm->resources()->getFileData(file, &size);
+ if (!fileData)
+ return false;
+
+ _useFullPalette = totalPalette;
+
+ uint32 compId = READ_BE_UINT32(fileData);
+
+ switch (compId) {
+ case kCompLZSS: {
+ uint32 dstsize = READ_LE_UINT32(fileData + 4);
+ _data = new uint8[dstsize];
+ decompressLZSS(fileData + 8, _data, dstsize);
+
+ // size can only be 640x400 or 1280x400
+ if (dstsize > 640 * 400 + 768)
+ _width = 1280;
+ else
+ _width = 640;
+
+ _height = 400;
+
+ // do we have a palette ?
+ _paletteEntries = (dstsize & 0x7ff) / 3;
+ if (_paletteEntries) {
+ _palette = new uint8[_paletteEntries * 3];
+ memcpy(_palette, _data + dstsize - (dstsize & 0x7ff), _paletteEntries * 3);
+ _vm->fixPaletteEntries(_palette, _paletteEntries);
+ } else {
+ _palette = 0;
+ }
+ return true;
+ }
+ case kCompSPCN: {
+ uint32 decSize = READ_LE_UINT32(fileData + 10);
+ _data = new uint8[decSize+100];
+ _paletteEntries = READ_LE_UINT16(fileData + 14) / 3;
+
+ if (_paletteEntries) {
+ _palette = new uint8[_paletteEntries * 3];
+ memcpy(_palette, fileData + 16, _paletteEntries * 3);
+ _vm->fixPaletteEntries(_palette, _paletteEntries);
+ }
+
+ // size can only be 640x400 or 1280x400
+ if (decSize > 640 * 400 + 768)
+ _width = 1280;
+ else
+ _width = 640;
+
+ _height = 400;
+
+ // decompress the picture into our buffer
+ decompressSPCN(fileData + 16 + _paletteEntries * 3, _data, decSize);
+ return true;
+ }
+ case kCompRNC1: {
+ Toon::RncDecoder rnc;
+
+ // allocate enough place
+ uint32 decSize = READ_BE_UINT32(fileData + 4);
+
+ _data = new uint8[decSize];
+ rnc.unpackM1(fileData, _data);
+
+ // size can only be 640x400 or 1280x400
+ if (decSize > 640 * 400 + 768)
+ _width = 1280;
+ else
+ _width = 640;
+
+ _height = 400;
+ return true;
+ }
+ case kCompRNC2: {
+ Toon::RncDecoder rnc;
+
+ // allocate enough place
+ uint32 decSize = READ_BE_UINT32(fileData + 4);
+
+ _data = new uint8[decSize];
+
+ decSize = rnc.unpackM2(fileData, _data);
+
+ if (decSize > 640 * 400 + 768)
+ _width = 1280;
+ else
+ _width = 640;
+
+ _height = 400;
+ return true;
+ }
+ }
+ return false;
+}
+
+Picture::Picture(ToonEngine *vm) : _vm(vm) {
+
+}
+
+void Picture::setupPalette() {
+ debugC(1, kDebugPicture, "setupPalette()");
+
+ if (_useFullPalette)
+ _vm->setPaletteEntries(_palette, 0, 256);
+ else
+ _vm->setPaletteEntries(_palette, 1, 128);
+}
+
+void Picture::drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) {
+ debugC(1, kDebugPicture, "drawMask(surface, %d, %d, %d, %d)", x, y, dx, dy);
+
+ for (int32 i = 0; i < 128; i++) {
+ byte color[3];
+ color[0] = i * 2;
+ color[1] = i * 2;
+ color[2] = 255 - i * 2;
+ _vm->setPaletteEntries(color, i, 1);
+ }
+
+ int32 rx = MIN(_width, surface.w - x);
+ int32 ry = MIN(_height, surface.h - y);
+
+ if (rx < 0 || ry < 0)
+ return;
+
+ int32 destPitch = surface.pitch;
+ int32 srcPitch = _width;
+ uint8 *c = _data + _width * dy + dx;
+ uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x;
+
+ for (int32 yy = 0; yy < ry; yy++) {
+ uint8 *curSrc = c;
+ uint8 *cur = curRow;
+ for (int32 xx = 0; xx < rx; xx++) {
+ //*cur = (*curSrc >> 5) * 8; // & 0x1f;
+ *cur = (*curSrc & 0x1f) ? 127 : 0;
+
+ curSrc++;
+ cur++;
+ }
+ curRow += destPitch;
+ c += srcPitch;
+ }
+}
+
+void Picture::draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy) {
+ debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy);
+
+ int32 rx = MIN(_width, surface.w - x);
+ int32 ry = MIN(_height, surface.h - y);
+
+ if (rx < 0 || ry < 0)
+ return;
+
+ int32 destPitch = surface.pitch;
+ int32 srcPitch = _width;
+ uint8 *c = _data + _width * dy + dx;
+ uint8 *curRow = (uint8 *)surface.pixels + y * destPitch + x;
+
+ for (int32 yy = 0; yy < ry; yy++) {
+ uint8 *curSrc = c;
+ uint8 *cur = curRow;
+ for (int32 xx = 0; xx < rx; xx++) {
+ *cur = *curSrc;
+ curSrc++;
+ cur++;
+ }
+ curRow += destPitch;
+ c += srcPitch;
+ }
+}
+
+uint8 Picture::getData(int32 x, int32 y) {
+ debugC(6, kDebugPicture, "getData(%d, %d)", x, y);
+
+ if (!_data)
+ return 0;
+
+ return _data[y * _width + x];
+}
+
+// use original work from johndoe
+void Picture::floodFillNotWalkableOnMask(int32 x, int32 y) {
+ debugC(1, kDebugPicture, "floodFillNotWalkableOnMask(%d, %d)", x, y);
+
+ // Stack-based floodFill algorithm based on
+ // http://student.kuleuven.be/~m0216922/CG/files/floodfill.cpp
+ Common::Stack<Common::Point> stack;
+ bool spanLeft, spanRight;
+ stack.push(Common::Point(x, y));
+ while (!stack.empty()) {
+ Common::Point pt = stack.pop();
+ while (_data[pt.x + pt.y * _width] & 0x1F && pt.y >= 0)
+ pt.y--;
+ pt.y++;
+ spanLeft = false;
+ spanRight = false;
+ while (_data[pt.x + pt.y * _width] & 0x1F && pt.y < _height) {
+ _data[pt.x + pt.y * _width] &= 0xE0;
+ if (!spanLeft && pt.x > 0 && _data[pt.x - 1 + pt.y * _width] & 0x1F) {
+ stack.push(Common::Point(pt.x - 1, pt.y));
+ spanLeft = 1;
+ } else if (spanLeft && pt.x > 0 && !(_data[pt.x - 1 + pt.y * _width] & 0x1F)) {
+ spanLeft = 0;
+ }
+ if (!spanRight && pt.x < _width - 1 && _data[pt.x + 1 + pt.y * _width] & 0x1F) {
+ stack.push(Common::Point(pt.x + 1, pt.y));
+ spanRight = 1;
+ } else if (spanRight && pt.x < _width - 1 && !(_data[pt.x + 1 + pt.y * _width] & 0x1F)) {
+ spanRight = 0;
+ }
+ pt.y++;
+ }
+ }
+}
+
+void Picture::drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable) {
+ debugC(1, kDebugPicture, "drawLineOnMask(%d, %d, %d, %d, %d)", x, y, x2, y2, (walkable) ? 1 : 0);
+
+ static int32 lastX = 0;
+ static int32 lastY = 0;
+
+ if (x == -1) {
+ x = lastX;
+ y = lastY;
+ }
+
+ uint32 bx = x << 16;
+ int32 dx = x2 - x;
+ uint32 by = y << 16;
+ int32 dy = y2 - y;
+ uint32 adx = abs(dx);
+ uint32 ady = abs(dy);
+ int32 t = 0;
+ if (adx <= ady)
+ t = ady;
+ else
+ t = adx;
+
+
+ int32 cdx = (dx << 16) / t;
+ int32 cdy = (dy << 16) / t;
+
+ int32 i = t;
+ while (i) {
+ if (!walkable) {
+ _data[_width * (by >> 16) + (bx >> 16)] &= 0xe0;
+ _data[_width * (by >> 16) + (bx >> 16)+1] &= 0xe0;
+ } else {
+ int32 v = _data[_width * (by >> 16) + (bx >> 16) - 1];
+ _data[_width * (by >> 16) + (bx >> 16)] = v;
+ _data[_width * (by >> 16) + (bx >> 16)+1] = v;
+ }
+
+ bx += cdx;
+ by += cdy;
+ i--;
+ }
+}
+} // End of namespace Toon
diff --git a/engines/toon/picture.h b/engines/toon/picture.h
new file mode 100644
index 0000000000..5065843b3c
--- /dev/null
+++ b/engines/toon/picture.h
@@ -0,0 +1,66 @@
+/* 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 TOON_PICTURE_H
+#define TOON_PICTURE_H
+
+#include "common/stream.h"
+#include "common/array.h"
+#include "common/func.h"
+#include "common/str.h"
+
+#include "toon/toon.h"
+
+namespace Toon {
+
+class ToonEngine;
+class Picture {
+
+public:
+ Picture(ToonEngine *vm);
+ bool loadPicture(Common::String file, bool totalPalette = false);
+ void setupPalette();
+ void draw(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy);
+ void drawMask(Graphics::Surface &surface, int32 x, int32 y, int32 dx, int32 dy);
+ void drawLineOnMask(int32 x, int32 y, int32 x2, int32 y2, bool walkable);
+ void floodFillNotWalkableOnMask(int32 x, int32 y);
+ uint8 getData(int32 x, int32 y);
+ uint8 *getDataPtr() { return _data; }
+ int32 getWidth() const { return _width; }
+ int32 getHeight() const { return _height; }
+protected:
+ int32 _width;
+ int32 _height;
+ uint8 *_data;
+ uint8 *_palette; // need to be copied at 3-387
+ int32 _paletteEntries;
+ bool _useFullPalette;
+
+ ToonEngine *_vm;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/resource.cpp b/engines/toon/resource.cpp
new file mode 100644
index 0000000000..348aa45ae9
--- /dev/null
+++ b/engines/toon/resource.cpp
@@ -0,0 +1,215 @@
+/* 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 "toon/resource.h"
+#include "common/file.h"
+#include "toon/toon.h"
+
+
+namespace Toon {
+
+void Resources::openPackage(Common::String fileName, bool preloadEntirePackage) {
+ debugC(1, kDebugResource, "openPackage(%s, %d)", fileName.c_str(), (preloadEntirePackage) ? 1 : 0);
+
+ Common::File file;
+ bool opened = file.open(fileName);
+
+ if (!opened)
+ return;
+
+
+ PakFile *pakFile = new PakFile();
+ pakFile->open(&file, fileName, preloadEntirePackage);
+
+ if (preloadEntirePackage)
+ file.close();
+
+ _pakFiles.push_back(pakFile);
+}
+
+void Resources::closePackage(Common::String fileName) {
+ for (uint32 i = 0; i < _pakFiles.size(); i++) {
+ if (_pakFiles[i]->getPackName() == fileName) {
+ delete _pakFiles[i];
+ _pakFiles.remove_at(i);
+ return;
+ }
+ }
+}
+
+Resources::Resources(ToonEngine *vm) : _vm(vm) {
+
+}
+
+uint8 *Resources::getFileData(Common::String fileName, uint32 *fileSize) {
+ debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.c_str());
+
+ // first try to find files outside of .pak
+ // some patched files have not been included in package.
+ if (Common::File::exists(fileName)) {
+ Common::File file;
+ bool opened = file.open(fileName);
+ if (!opened)
+ return 0;
+
+ *fileSize = file.size();
+ uint8 *memory = (uint8 *)new uint8[*fileSize];
+ file.read(memory, *fileSize);
+ file.close();
+ return memory;
+ } else {
+ for (uint32 i = 0; i < _pakFiles.size(); i++) {
+ uint32 locFileSize = 0;
+ uint8 *locFileData = 0;
+
+ locFileData = _pakFiles[i]->getFileData(fileName, &locFileSize);
+ if (locFileData) {
+ *fileSize = locFileSize;
+ return locFileData;
+ }
+ }
+ return 0;
+ }
+}
+
+Common::SeekableReadStream *Resources::openFile(Common::String fileName) {
+ debugC(1, kDebugResource, "openFile(%s)", fileName.c_str());
+
+ // first try to find files outside of .pak
+ // some patched files have not been included in package.
+ if (Common::File::exists(fileName)) {
+ Common::File *file = new Common::File();
+ bool opened = file->open(fileName);
+ if (!opened) {
+ delete file;
+ return 0;
+ }
+ return file;
+ } else {
+ for (uint32 i = 0; i < _pakFiles.size(); i++) {
+ Common::SeekableReadStream *stream = 0;
+ stream = _pakFiles[i]->createReadStream(fileName);
+ if (stream)
+ return stream;
+ }
+
+ return 0;
+ }
+}
+Common::SeekableReadStream *PakFile::createReadStream(Common::String fileName) {
+ debugC(1, kDebugResource, "createReadStream(%s)", fileName.c_str());
+
+ int32 offset = 0;
+ int32 size = 0;
+ for (uint32 i = 0; i < _numFiles; i++) {
+ if (fileName.compareToIgnoreCase(_files[i]._name) == 0) {
+ size = _files[i]._size;
+ offset = _files[i]._offset;
+ break;
+ }
+ }
+ if (!size)
+ return 0;
+
+ if (_fileHandle)
+ return new Common::SeekableSubReadStream(_fileHandle, offset, offset + size);
+ else
+ return new Common::MemoryReadStream(_buffer + offset, size);
+}
+
+uint8 *PakFile::getFileData(Common::String fileName, uint32 *fileSize) {
+ debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.c_str());
+
+ for (uint32 i = 0; i < _numFiles; i++) {
+ if (fileName.compareToIgnoreCase(_files[i]._name) == 0) {
+ *fileSize = _files[i]._size;
+ return _buffer + _files[i]._offset;
+ }
+ }
+
+ return 0;
+}
+
+void PakFile::open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage) {
+ debugC(1, kDebugResource, "open(rs, %d)", (preloadEntirePackage) ? 1 : 0);
+
+ char buffer[64];
+ int32 currentPos = 0;
+ _numFiles = 0;
+ _packName = packName;
+
+ while (1) {
+ rs->seek(currentPos);
+ rs->read(buffer, 64);
+
+ int32 offset = READ_LE_UINT32(buffer);
+ char *name = buffer + 4;
+
+ if (!*name)
+ break;
+
+ int32 nameSize = strlen(name) + 1;
+ int32 nextOffset = READ_LE_UINT32(buffer + 4 + nameSize);
+ currentPos += 4 + nameSize;
+
+ PakFile::File newFile;
+ strcpy(newFile._name, name);
+ newFile._offset = offset;
+ newFile._size = nextOffset - offset;
+ _numFiles++;
+ _files.push_back(newFile);
+ }
+
+ if (preloadEntirePackage) {
+ _bufferSize = rs->size();
+ _buffer = new uint8[_bufferSize];
+ rs->seek(0);
+ rs->read(_buffer, _bufferSize);
+ }
+}
+
+void PakFile::close() {
+ if (_buffer) {
+ delete[] _buffer;
+ }
+
+ if (_fileHandle) {
+ _fileHandle->close();
+ delete _fileHandle;
+ }
+}
+
+PakFile::~PakFile() {
+ close();
+}
+
+
+PakFile::PakFile() {
+ _fileHandle = 0;
+ _buffer = 0;
+ _bufferSize = 0;
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/resource.h b/engines/toon/resource.h
new file mode 100644
index 0000000000..3a080fe894
--- /dev/null
+++ b/engines/toon/resource.h
@@ -0,0 +1,82 @@
+/* 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 TOON_RESOURCE_H
+#define TOON_RESOURCE_H
+
+#include "common/array.h"
+#include "common/str.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+namespace Toon {
+
+class PakFile {
+public:
+ PakFile();
+ ~PakFile();
+
+ void open(Common::SeekableReadStream *rs, Common::String packName, bool preloadEntirePackage);
+ uint8 *getFileData(Common::String fileName, uint32 *fileSize);
+ Common::String getPackName() { return _packName; }
+ Common::SeekableReadStream *createReadStream(Common::String fileName);
+ void close();
+
+protected:
+ struct File {
+ char _name[13];
+ int32 _offset;
+ int32 _size;
+ };
+ Common::String _packName;
+
+ uint8 *_buffer;
+ int32 _bufferSize;
+
+ uint32 _numFiles;
+ Common::Array<File> _files;
+ Common::File *_fileHandle;
+
+
+};
+
+class ToonEngine;
+
+class Resources {
+public:
+ Resources(ToonEngine *vm);
+ void openPackage(Common::String file, bool preloadEntirePackage);
+ void closePackage(Common::String fileName);
+ Common::SeekableReadStream *openFile(Common::String file);
+ uint8 *getFileData(Common::String fileName, uint32 *fileSize);
+
+protected:
+ ToonEngine *_vm;
+ Common::Array<PakFile *> _pakFiles;
+
+};
+
+} // End of namespace Toon
+#endif
diff --git a/engines/toon/script.cpp b/engines/toon/script.cpp
new file mode 100644
index 0000000000..06d482f4e2
--- /dev/null
+++ b/engines/toon/script.cpp
@@ -0,0 +1,504 @@
+/* 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/endian.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+
+#include "toon/toon.h"
+#include "toon/script.h"
+
+namespace Toon {
+EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filename(0) {
+
+#define OPCODE(x) { &EMCInterpreter::x, #x }
+ static const OpcodeEntry opcodes[] = {
+ // 0x00
+ OPCODE(op_jmp),
+ OPCODE(op_setRetValue),
+ OPCODE(op_pushRetOrPos),
+ OPCODE(op_push),
+ // 0x04
+ OPCODE(op_push),
+ OPCODE(op_pushReg),
+ OPCODE(op_pushBPNeg),
+ OPCODE(op_pushBPAdd),
+ // 0x08
+ OPCODE(op_popRetOrPos),
+ OPCODE(op_popReg),
+ OPCODE(op_popBPNeg),
+ OPCODE(op_popBPAdd),
+ // 0x0C
+ OPCODE(op_addSP),
+ OPCODE(op_subSP),
+ OPCODE(op_sysCall),
+ OPCODE(op_ifNotJmp),
+ // 0x10
+ OPCODE(op_negate),
+ OPCODE(op_eval),
+ OPCODE(op_setRetAndJmp)
+ };
+ _opcodes = opcodes;
+#undef OPCODE
+}
+
+bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
+ switch (chunk._type) {
+ case MKID_BE('TEXT'):
+ _scriptData->text = new byte[chunk._size];
+ assert(_scriptData->text);
+ if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size)
+ error("Couldn't read TEXT chunk from file '%s'", _filename);
+ break;
+
+ case MKID_BE('ORDR'):
+ _scriptData->ordr = new uint16[chunk._size >> 1];
+ assert(_scriptData->ordr);
+ if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size)
+ error("Couldn't read ORDR chunk from file '%s'", _filename);
+
+ for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
+ _scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]);
+ break;
+
+ case MKID_BE('DATA'):
+ _scriptData->data = new uint16[chunk._size >> 1];
+ assert(_scriptData->data);
+ if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size)
+ error("Couldn't read DATA chunk from file '%s'", _filename);
+
+ for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
+ _scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]);
+ break;
+
+ default:
+ warning("Unexpected chunk '%s' of size %d found in file '%s'", Common::tag2string(chunk._type).c_str(), chunk._size, _filename);
+ }
+
+ return false;
+}
+
+bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode *> *opcodes) {
+ Common::SeekableReadStream *stream = _vm->resources()->openFile(filename);
+ if (!stream) {
+ error("Couldn't open script file '%s'", filename);
+ return false; // for compilers that don't support NORETURN
+ }
+
+ memset(scriptData, 0, sizeof(EMCData));
+
+ _scriptData = scriptData;
+ _filename = filename;
+
+ IFFParser iff(*stream);
+ Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback);
+ iff.parse(c);
+
+ if (!_scriptData->ordr)
+ error("No ORDR chunk found in file: '%s'", filename);
+
+ if (!_scriptData->data)
+ error("No DATA chunk found in file: '%s'", filename);
+
+ if (stream->err())
+ error("Read error while parsing file '%s'", filename);
+
+ delete stream;
+
+ _scriptData->sysFuncs = opcodes;
+
+ strncpy(_scriptData->filename, filename, 13);
+ _scriptData->filename[12] = 0;
+
+ _scriptData = 0;
+ _filename = 0;
+
+ return true;
+}
+
+void EMCInterpreter::unload(EMCData *data) {
+ if (!data)
+ return;
+
+ delete[] data->text;
+ delete[] data->ordr;
+ delete[] data->data;
+
+ data->text = 0;
+ data->ordr = data->data = 0;
+}
+
+void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) {
+ scriptStat->dataPtr = data;
+ scriptStat->ip = 0;
+ scriptStat->stack[EMCState::kStackLastEntry] = 0;
+ scriptStat->bp = EMCState::kStackSize + 1;
+ scriptStat->sp = EMCState::kStackLastEntry;
+ scriptStat->running = false;
+}
+
+bool EMCInterpreter::start(EMCState *script, int function) {
+ if (!script->dataPtr)
+ return false;
+
+ uint16 functionOffset = script->dataPtr->ordr[function];
+ if (functionOffset == 0xFFFF)
+ return false;
+
+ script->ip = &script->dataPtr->data[functionOffset+1];
+
+ return true;
+}
+
+bool EMCInterpreter::isValid(EMCState *script) {
+ if (!script->ip || !script->dataPtr || _vm->shouldQuitGame())
+ return false;
+ return true;
+}
+
+bool EMCInterpreter::run(EMCState *script) {
+
+ if (script->running)
+ return false;
+
+ _parameter = 0;
+
+ if (!script->ip)
+ return false;
+
+ script->running = true;
+
+
+ // Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original
+ // would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway.
+ const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data);
+ int16 code = *script->ip++;
+ int16 opcode = (code >> 8) & 0x1F;
+
+ if (code & 0x8000) {
+ opcode = 0;
+ _parameter = code & 0x7FFF;
+ } else if (code & 0x4000) {
+ _parameter = (int8)(code);
+ } else if (code & 0x2000) {
+ _parameter = *script->ip++;
+ } else {
+ _parameter = 0;
+ }
+
+ if (opcode > 18) {
+ error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset);
+ } else {
+ static bool EMCDebug = false;
+ if (EMCDebug)
+ debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter);
+ //debug(0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
+
+ (this->*(_opcodes[opcode].proc))(script);
+ }
+
+ script->running = false;
+ return (script->ip != 0);
+}
+
+#pragma mark -
+#pragma mark - Command implementations
+#pragma mark -
+
+void EMCInterpreter::op_jmp(EMCState *script) {
+ script->ip = script->dataPtr->data + _parameter;
+}
+
+void EMCInterpreter::op_setRetValue(EMCState *script) {
+ script->retValue = _parameter;
+}
+
+void EMCInterpreter::op_pushRetOrPos(EMCState *script) {
+ switch (_parameter) {
+ case 0:
+ script->stack[--script->sp] = script->retValue;
+ break;
+
+ case 1:
+ script->stack[--script->sp] = script->ip - script->dataPtr->data + 1;
+ script->stack[--script->sp] = script->bp;
+ script->bp = script->sp + 2;
+ break;
+
+ default:
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_push(EMCState *script) {
+ script->stack[--script->sp] = _parameter;
+}
+
+void EMCInterpreter::op_pushReg(EMCState *script) {
+ script->stack[--script->sp] = script->regs[_parameter];
+}
+
+void EMCInterpreter::op_pushBPNeg(EMCState *script) {
+ script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
+}
+
+void EMCInterpreter::op_pushBPAdd(EMCState *script) {
+ script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
+}
+
+void EMCInterpreter::op_popRetOrPos(EMCState *script) {
+ switch (_parameter) {
+ case 0:
+ script->retValue = script->stack[script->sp++];
+ break;
+
+ case 1:
+ if (script->sp >= EMCState::kStackLastEntry) {
+ script->ip = 0;
+ } else {
+ script->bp = script->stack[script->sp++];
+ script->ip = script->dataPtr->data + script->stack[script->sp++];
+ }
+ break;
+
+ default:
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_popReg(EMCState *script) {
+ script->regs[_parameter] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_popBPNeg(EMCState *script) {
+ script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_popBPAdd(EMCState *script) {
+ script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_addSP(EMCState *script) {
+ script->sp += _parameter;
+}
+
+void EMCInterpreter::op_subSP(EMCState *script) {
+ script->sp -= _parameter;
+}
+
+void EMCInterpreter::op_sysCall(EMCState *script) {
+ const uint8 id = _parameter;
+
+ assert(script->dataPtr->sysFuncs);
+ assert(id < script->dataPtr->sysFuncs->size());
+
+ if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) {
+ script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script);
+ } else {
+ script->retValue = 0;
+ warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename);
+ }
+}
+
+void EMCInterpreter::op_ifNotJmp(EMCState *script) {
+ if (!script->stack[script->sp++]) {
+ _parameter &= 0x7FFF;
+ script->ip = script->dataPtr->data + _parameter;
+ }
+}
+
+void EMCInterpreter::op_negate(EMCState *script) {
+ int16 value = script->stack[script->sp];
+ switch (_parameter) {
+ case 0:
+ if (!value)
+ script->stack[script->sp] = 1;
+ else
+ script->stack[script->sp] = 0;
+ break;
+
+ case 1:
+ script->stack[script->sp] = -value;
+ break;
+
+ case 2:
+ script->stack[script->sp] = ~value;
+ break;
+
+ default:
+ warning("Unknown negation func: %d", _parameter);
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_eval(EMCState *script) {
+ int16 ret = 0;
+ bool error = false;
+
+ int16 val1 = script->stack[script->sp++];
+ int16 val2 = script->stack[script->sp++];
+
+ switch (_parameter) {
+ case 0:
+ ret = (val2 && val1) ? 1 : 0;
+ break;
+
+ case 1:
+ ret = (val2 || val1) ? 1 : 0;
+ break;
+
+ case 2:
+ ret = (val1 == val2) ? 1 : 0;
+ break;
+
+ case 3:
+ ret = (val1 != val2) ? 1 : 0;
+ break;
+
+ case 4:
+ ret = (val1 > val2) ? 1 : 0;
+ break;
+
+ case 5:
+ ret = (val1 >= val2) ? 1 : 0;
+ break;
+
+ case 6:
+ ret = (val1 < val2) ? 1 : 0;
+ break;
+
+ case 7:
+ ret = (val1 <= val2) ? 1 : 0;
+ break;
+
+ case 8:
+ ret = val1 + val2;
+ break;
+
+ case 9:
+ ret = val2 - val1;
+ break;
+
+ case 10:
+ ret = val1 * val2;
+ break;
+
+ case 11:
+ ret = val2 / val1;
+ break;
+
+ case 12:
+ ret = val2 >> val1;
+ break;
+
+ case 13:
+ ret = val2 << val1;
+ break;
+
+ case 14:
+ ret = val1 & val2;
+ break;
+
+ case 15:
+ ret = val1 | val2;
+ break;
+
+ case 16:
+ ret = val2 % val1;
+ break;
+
+ case 17:
+ ret = val1 ^ val2;
+ break;
+
+ default:
+ warning("Unknown evaluate func: %d", _parameter);
+ error = true;
+ }
+
+ if (error)
+ script->ip = 0;
+ else
+ script->stack[--script->sp] = ret;
+}
+
+void EMCInterpreter::op_setRetAndJmp(EMCState *script) {
+ if (script->sp >= EMCState::kStackLastEntry) {
+ script->ip = 0;
+ } else {
+ script->retValue = script->stack[script->sp++];
+ uint16 temp = script->stack[script->sp++];
+ script->stack[EMCState::kStackLastEntry] = 0;
+ script->ip = &script->dataPtr->data[temp];
+ }
+}
+
+void EMCInterpreter::saveState(EMCState *script, Common::WriteStream *stream) {
+ stream->writeSint16LE(script->bp);
+ stream->writeSint16LE(script->sp);
+ if (!script->ip) {
+ stream->writeSint16LE(-1);
+ } else {
+ stream->writeSint16LE(script->ip - script->dataPtr->data);
+ }
+
+ for (int32 i = 0; i < EMCState::kStackSize; i++) {
+ stream->writeSint16LE(script->stack[i]);
+ }
+
+ for (int32 i = 0; i < 30; i++) {
+ stream->writeSint16LE(script->regs[i]);
+ }
+
+ stream->writeSint16LE(script->retValue);
+ stream->writeByte(script->running);
+}
+void EMCInterpreter::loadState(EMCState *script, Common::ReadStream *stream) {
+ script->bp = stream->readSint16LE();
+ script->sp = stream->readSint16LE();
+
+ int16 scriptIp = stream->readSint16LE();
+ if (scriptIp == -1) {
+ script->ip = 0;
+ } else {
+ script->ip = scriptIp + script->dataPtr->data;
+ }
+
+ for (int32 i = 0; i < EMCState::kStackSize; i++) {
+ script->stack[i] = stream->readSint16LE();
+ }
+
+ for (int32 i = 0; i < 30; i++) {
+ script->regs[i] = stream->readSint16LE();
+ }
+
+ script->retValue = stream->readSint16LE();
+ script->running = stream->readByte();
+}
+
+} // End of namespace Toon
+
diff --git a/engines/toon/script.h b/engines/toon/script.h
new file mode 100644
index 0000000000..47e04e8c82
--- /dev/null
+++ b/engines/toon/script.h
@@ -0,0 +1,152 @@
+/* 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 TOON_SCRIPT_H
+#define TOON_SCRIPT_H
+
+#include "common/stream.h"
+#include "common/array.h"
+#include "common/func.h"
+#include "common/iff_container.h"
+
+
+// Based on Kyra script interpretor
+namespace Toon {
+
+struct EMCState;
+typedef Common::Functor1<EMCState *, int> Opcode;
+
+struct EMCData {
+ char filename[13];
+
+ byte *text;
+ uint16 *data;
+ uint16 *ordr;
+ uint16 dataSize;
+
+ const Common::Array<const Opcode *> *sysFuncs;
+};
+
+struct EMCState {
+ enum {
+ kStackSize = 100,
+ kStackLastEntry = kStackSize - 1
+ };
+
+ const uint16 *ip;
+ const EMCData *dataPtr;
+ int16 retValue;
+ uint16 bp;
+ uint16 sp;
+ int16 regs[30]; // VM registers
+ int16 stack[kStackSize]; // VM stack
+ bool running;
+};
+
+#define stackPos(x) (state->stack[state->sp+x])
+#define stackPosString(x) ((const char *)&state->dataPtr->text[READ_BE_UINT16(&state->dataPtr->text[stackPos(x)<<1])])
+
+class Resource;
+class ToonEngine;
+
+class IFFParser : public Common::IFFParser {
+public:
+ IFFParser(Common::ReadStream &input) : Common::IFFParser(&input) {
+ // It seems Westwood missunderstood the 'size' field of the FORM chunk.
+ //
+ // For EMC scripts (type EMC2) it's filesize instead of filesize - 8,
+ // means accidently including the 8 bytes used by the chunk header for the FORM
+ // chunk.
+ //
+ // For TIM scripts (type AVFS) it's filesize - 12 instead of filesize - 8,
+ // means it will not include the size of the 'type' field in the FORM chunk,
+ // instead of only not including the chunk header size.
+ //
+ // Both lead to some problems in our IFF parser, either reading after the end
+ // of file or producing a "Chunk overread" error message. To work around this
+ // we need to adjust the size field properly.
+ if (_formType == MKID_BE('EMC2'))
+ _formChunk.size -= 8;
+ else if (_formType == MKID_BE('AVFS'))
+ _formChunk.size += 4;
+ }
+};
+
+class EMCInterpreter {
+public:
+ EMCInterpreter(ToonEngine *vm);
+
+ bool load(const char *filename, EMCData *data, const Common::Array<const Opcode *> *opcodes);
+ void unload(EMCData *data);
+
+ void init(EMCState *scriptState, const EMCData *data);
+ bool start(EMCState *script, int function);
+
+ void saveState(EMCState *script, Common::WriteStream *stream);
+ void loadState(EMCState *script, Common::ReadStream *stream);
+
+ bool isValid(EMCState *script);
+
+ bool run(EMCState *script);
+protected:
+ ToonEngine *_vm;
+ int16 _parameter;
+
+ const char *_filename;
+ EMCData *_scriptData;
+
+ bool callback(Common::IFFChunk &chunk);
+
+ typedef void (EMCInterpreter::*OpcodeProc)(EMCState *);
+ struct OpcodeEntry {
+ OpcodeProc proc;
+ const char *desc;
+ };
+
+ const OpcodeEntry *_opcodes;
+private:
+ void op_jmp(EMCState *);
+ void op_setRetValue(EMCState *);
+ void op_pushRetOrPos(EMCState *);
+ void op_push(EMCState *);
+ void op_pushReg(EMCState *);
+ void op_pushBPNeg(EMCState *);
+ void op_pushBPAdd(EMCState *);
+ void op_popRetOrPos(EMCState *);
+ void op_popReg(EMCState *);
+ void op_popBPNeg(EMCState *);
+ void op_popBPAdd(EMCState *);
+ void op_addSP(EMCState *);
+ void op_subSP(EMCState *);
+ void op_sysCall(EMCState *);
+ void op_ifNotJmp(EMCState *);
+ void op_negate(EMCState *);
+ void op_eval(EMCState *);
+ void op_setRetAndJmp(EMCState *);
+};
+} // End of namespace Toon
+
+#endif
+
diff --git a/engines/toon/script_func.cpp b/engines/toon/script_func.cpp
new file mode 100644
index 0000000000..821a8971de
--- /dev/null
+++ b/engines/toon/script_func.cpp
@@ -0,0 +1,1154 @@
+/* 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 "toon/script_func.h"
+#include "toon/script.h"
+#include "toon/state.h"
+#include "toon/toon.h"
+#include "toon/anim.h"
+#include "toon/hotspot.h"
+#include "toon/drew.h"
+#include "toon/flux.h"
+
+namespace Toon {
+
+typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
+#define SetOpcodeTable(x) table = &x;
+#define Opcode(x) table->push_back(new OpcodeV2(this, &ScriptFunc::x))
+#define OpcodeUnImpl() table->push_back(new OpcodeV2(this, 0))
+
+ScriptFunc::ScriptFunc(ToonEngine *vm) {
+ Common::Array<const Opcode *> *table = 0;
+
+ _vm = vm;
+ _opcodes.reserve(176);
+ SetOpcodeTable(_opcodes);
+
+ Opcode(sys_Cmd_Dummy); // dd offset sub_2B160
+ Opcode(sys_Cmd_Change_Actor_X_And_Y); // dd offset sub_2A710
+ Opcode(sys_Cmd_Init_Talking_Character); // dd offset sub_2A4E0
+ Opcode(sys_Cmd_Draw_Actor_Standing), // dd offset sub_2A650
+ Opcode(sys_Cmd_Get_Actor_X), // dd offset sub_2ADC0
+ Opcode(sys_Cmd_Get_Actor_Y), // dd offset sub_2ADD0
+ Opcode(sys_Cmd_Get_Actor_Facing), // dd offset sub_2A790
+ Opcode(sys_Cmd_Get_Last_Scene), // dd offset sub_29F80
+ Opcode(sys_Cmd_Debug_Print), // dd offset sub_2A510
+ Opcode(sys_Cmd_Flip_Screens), // dd offset sub_2A180
+ Opcode(sys_Cmd_Play_Flic), // dd offset sub_2A080
+ Opcode(sys_Cmd_Force_Facing), // dd offset sub_29F90
+ Opcode(sys_Cmd_Restart_Thread), // dd offset sub_29F30
+ Opcode(sys_Cmd_Walk_Actor_To_Point), // dd offset sub_2A440
+ Opcode(sys_Cmd_Set_Sack_Visible), // dd offset sub_29920
+ Opcode(sys_Cmd_Set_Actor_Facing), // dd offset sub_2AD60
+ Opcode(sys_Cmd_Confiscate_Inventory), // dd offset sub_29EB0
+ Opcode(sys_Cmd_Character_Talks), // dd offset sub_29F00
+ Opcode(sys_Cmd_Visited_Scene), // dd offset sub_29E80
+ Opcode(sys_Cmd_Query_Rif_Flag), // dd offset sub_29D20
+ Opcode(sys_Cmd_Query_Scroll), // dd offset sub_29D60
+ Opcode(sys_Cmd_Set_Initial_Location), // dd offset sub_2AD80
+ Opcode(sys_Cmd_Make_Line_Non_Walkable), // dd offset sub_29FC0
+ Opcode(sys_Cmd_Make_Line_Walkable), // dd offset sub_2A050
+ Opcode(sys_Cmd_Walk_Actor_On_Condition), // dd offset sub_29D70
+ Opcode(sys_Cmd_Set_Actor_Facing_Point), // dd offset sub_29E60
+ Opcode(sys_Cmd_Set_Inventory_Slot), // dd offset sub_2B0D0
+ Opcode(sys_Cmd_Get_Inventory_Slot), // dd offset sub_2B0F0
+ Opcode(sys_Cmd_Add_Item_To_Inventory), // dd offset sub_2AE50
+ Opcode(sys_Cmd_Set_Actor_RGB_Modifiers), // dd offset sub_29CA0
+ Opcode(sys_Cmd_Init_Conversation_AP), // dd offset sub_2B130
+ Opcode(sys_Cmd_Actor_Talks), // dd offset sub_2ADA0
+ Opcode(sys_Cmd_Say_Lines), // dd offset sub_29B20
+ Opcode(sys_Cmd_Set_Rif_Flag), // dd offset sub_2A320
+ Opcode(sys_Cmd_Empty_Inventory), // dd offset sub_2AE10
+ Opcode(sys_Cmd_Set_Anim_Scale_Size), // dd offset sub_29BD0
+ Opcode(sys_Cmd_Delete_Item_From_Inventory), // dd offset sub_2AE70
+ Opcode(sys_Cmd_Specific_Item_In_Inventory), // dd offset sub_2A740
+ Opcode(sys_Cmd_Run_Script), // dd offset sub_29AF0
+ Opcode(sys_Cmd_Query_Game_Flag), // dd offset sub_2A3E0
+ Opcode(sys_Cmd_Reset_Game_Flag), // dd offset sub_2A420
+ Opcode(sys_Cmd_Set_Game_Flag), // dd offset sub_2A400
+ Opcode(sys_Cmd_Create_Mouse_Item), // dd offset sub_2A4B0
+ Opcode(sys_Cmd_Destroy_Mouse_Item), // dd offset sub_2A4D0
+ Opcode(sys_Cmd_Get_Mouse_State), // dd offset sub_2A860
+ Opcode(sys_Cmd_Hide_Mouse), // dd offset sub_2A5D0
+ Opcode(sys_Cmd_Exit_Conversation), // dd offset sub_29AE0
+ Opcode(sys_Cmd_Set_Mouse_Pos), // dd offset sub_2A810
+ Opcode(sys_Cmd_Show_Mouse), // dd offset sub_2A5F0
+ Opcode(sys_Cmd_In_Close_Up), // dd offset sub_29FB0
+ Opcode(sys_Cmd_Set_Scroll_Lock), // dd offset sub_298B0
+ Opcode(sys_Cmd_Fill_Area_Non_Walkable), // dd offset sub_29FF0
+ Opcode(sys_Cmd_Set_Scroll_Coords), // dd offset sub_298D0
+ Opcode(sys_Cmd_Hide_Cutaway), // dd offset sub_2A0F0
+ Opcode(sys_Cmd_Show_Cutaway), // dd offset sub_2A100
+ Opcode(sys_Cmd_Pause_Ticks), // dd offset sub_2A360
+ Opcode(sys_Cmd_In_Conversation), // dd offset sub_29C60
+ Opcode(sys_Cmd_Character_Talking), // dd offset sub_29C70
+ Opcode(sys_Cmd_Set_Flux_Facing_Point), // dd offset sub_29980
+ Opcode(sys_Cmd_Set_Flux_Facing), // dd offset sub_299A0
+ Opcode(sys_Cmd_Set_Flux_Coords), // dd offset sub_299C0
+ Opcode(sys_Cmd_Set_Flux_Visible), // dd offset sub_299F0
+ Opcode(sys_Cmd_Get_Flux_X), // dd offset sub_29A40
+ Opcode(sys_Cmd_Get_Flux_Y), // dd offset sub_29A50
+ Opcode(sys_Cmd_Get_Flux_Facing), // dd offset sub_29A60
+ Opcode(sys_Cmd_Get_Flux_Flags), // dd offset sub_29A70
+ Opcode(sys_Cmd_Query_Flux_Coords), // dd offset sub_29A90
+ Opcode(sys_Cmd_Have_A_Conversation), // dd offset sub_2B110
+ Opcode(sys_Cmd_Walk_Flux_To_Point), // dd offset sub_29AC0
+ Opcode(sys_Cmd_Get_Actor_Final_X), // dd offset sub_29940
+ Opcode(sys_Cmd_Get_Actor_Final_Y), // dd offset sub_29960
+ Opcode(sys_Cmd_Query_Scene_Anim_Loaded), // dd offset sub_29870
+ Opcode(sys_Cmd_Play_Flux_Anim), // dd offset sub_29820
+ Opcode(sys_Cmd_Set_Anim_Priority), // dd offset sub_29790
+ Opcode(sys_Cmd_Place_Scene_Anim), // dd offset sub_2A7A0
+ Opcode(sys_Cmd_Update_Scene_Animations), // dd offset sub_2AE30
+ Opcode(sys_Cmd_Get_Drew_Scale), // dd offset sub_297E0
+ Opcode(sys_Cmd_Query_Drew_Flags), // dd offset sub_29800
+ Opcode(sys_Cmd_Set_Music), // dd offset sub_29720
+ Opcode(sys_Cmd_Query_Speech), // dd offset sub_296D0
+ Opcode(sys_Cmd_Enter_New_Scene), // dd offset sub_2A550
+ Opcode(sys_Cmd_Enter_Same_Scene), // dd offset sub_2ADE0
+ Opcode(sys_Cmd_Is_Pixel_Walkable), // dd offset sub_2A4F0
+ Opcode(sys_Cmd_Show_Screen), // dd offset sub_2A0C0
+ Opcode(sys_Cmd_Hide_Screen), // dd offset sub_2A0F0
+ Opcode(sys_Cmd_Dummy), // dd offset sub_295D0
+ Opcode(sys_Cmd_Set_Special_Enter_X_And_Y), // dd offset sub_2A590
+ Opcode(sys_Cmd_Get_Mouse_X), // dd offset sub_296B0
+ Opcode(sys_Cmd_Get_Mouse_Y), // dd offset sub_296C0
+ Opcode(sys_Cmd_Fade_Palette), // dd offset sub_29650
+ Opcode(sys_Cmd_Music_Enabled), // dd offset sub_29620
+ Opcode(sys_Cmd_Dummy), // dd offset sub_295F0
+ Opcode(sys_Cmd_Dummy), // dd offset sub_29610
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Random), // dd offset sub_2A600
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Wait_Key), // dd offset sub_2AE20
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back), // dd offset sub_2A940
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Set_Scene_Anim_Wait), // dd offset sub_2A870
+ Opcode(sys_Cmd_Init_Scene_Anim), // dd offset sub_2AC60
+ Opcode(sys_Cmd_Set_Scene_Animation_Active_Flag), // dd offset sub_2AB10
+ Opcode(sys_Cmd_Draw_Scene_Anim_WSA_Frame), // dd offset sub_2A8D0
+ Opcode(sys_Cmd_Move_Scene_Anim), // dd offset sub_2AA90
+ Opcode(sys_Cmd_Run_Actor_Default_Script), // dd offset sub_2A4E0
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Set_Location_Data), // dd offset sub_2AE90
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Set_CountDown_Timer), // dd offset sub_2AFC0
+ Opcode(sys_Cmd_Query_CountDown_Timer), // dd offset sub_2AFE0
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Proceed_To_Next_Chapter), // dd offset sub_2AFF0
+ Opcode(sys_Cmd_Play_Sfx_Plus), // dd offset sub_2A1D0
+ Opcode(sys_Cmd_Play_Sfx), // dd offset sub_2A1A0
+ Opcode(sys_Cmd_Set_Ambient_Sfx), // dd offset sub_2A260
+ Opcode(sys_Cmd_Kill_Ambient_Sfx), // dd offset sub_2A300
+ Opcode(sys_Cmd_Set_Ambient_Sfx_Plus), // dd offset sub_2A290
+ Opcode(sys_Cmd_Set_Ambient_Volume), // dd offset sub_2A240
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Freeze_Scene_Animation), // dd offset sub_2AB90
+ Opcode(sys_Cmd_Unfreeze_Scene_Animation), // dd offset sub_2ABB0
+ Opcode(sys_Cmd_Scene_Animation_Frozen), // dd offset sub_2ABD0
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Set_Script_Game_Data_Global), // dd offset sub_2ABF0
+ Opcode(sys_Cmd_Get_Script_Game_Data_Global), // dd offset sub_2AC30
+ Opcode(sys_Cmd_Say_Line), // dd offset loc_2A190
+ Opcode(sys_Cmd_Knight_Puzzle_Get_Coord), // dd offset sub_2A110
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Add_Scene_Anim), // dd offset sub_2AC60
+ Opcode(sys_Cmd_Remove_Scene_Anim), // dd offset sub_2ACE0
+ Opcode(sys_Cmd_Disable_Timer), // dd offset sub_2AD00
+ Opcode(sys_Cmd_Enable_Timer), // dd offset sub_2AD20
+ Opcode(sys_Cmd_Set_Timer), // dd offset sub_2AD40
+ Opcode(sys_Cmd_Set_Palette_Color), // dd offset sub_2B020
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Number_Of_NPCs), // dd offset loc_2A190
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Dummy), // dd offset sub_2B160
+ Opcode(sys_Cmd_Get_Config_Language), // dd offset sub_2B0C0
+ Opcode(sys_Cmd_Dummy); // dd offset sub_2B160
+}
+
+ScriptFunc::~ScriptFunc(void) {
+
+}
+
+char *GetText(int32 i, EMCState *state) {
+ short stack = stackPos(i);
+ unsigned short textoffset = READ_BE_UINT16(&((unsigned short *)(state->dataPtr->text))[stack]);
+ char *text = (char *)&state->dataPtr->text[textoffset];
+ return text;
+}
+
+int32 ScriptFunc::sys_Cmd_Dummy(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Change_Actor_X_And_Y(EMCState *state) {
+ _vm->getDrew()->setPosition(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Init_Talking_Character(EMCState *state) {
+ // really does nothing in original
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Draw_Actor_Standing(EMCState *state) {
+
+ int32 arg1 = stackPos(0);
+ int32 arg2 = stackPos(1);
+
+ if (arg2 > -1)
+ _vm->getDrew()->setFacing(arg2);
+
+ if (arg1 < 0) {
+ _vm->getDrew()->setVisible(false);
+ } else {
+ _vm->getDrew()->setVisible(true);
+ _vm->getDrew()->playStandingAnim();
+ }
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Actor_X(EMCState *state) {
+ return _vm->getDrew()->getX();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Actor_Y(EMCState *state) {
+ return _vm->getDrew()->getY();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Actor_Final_X(EMCState *state) {
+ return _vm->getDrew()->getFinalX();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Actor_Final_Y(EMCState *state) {
+ return _vm->getDrew()->getFinalY();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Actor_Facing(EMCState *state) {
+ return _vm->getDrew()->getFacing();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Last_Scene(EMCState *state) {
+ return _vm->state()->_lastVisitedScene;
+}
+
+int32 ScriptFunc::sys_Cmd_Debug_Print(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Flip_Screens(EMCState *state) {
+ _vm->flipScreens();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Play_Flic(EMCState *state) {
+
+ char name[256];
+
+ // workaround for the video of the beginning
+ if (strstr(GetText(0, state), "209"))
+ sprintf(name, "misc/%s", GetText(0, state));
+ else
+ strcpy(name, _vm->createRoomFilename(GetText(0, state)).c_str());
+
+// Strangerke - Commented (not used)
+// int32 Flags = stackPos(1);
+ int32 stopMusic = stackPos(2);
+ _vm->getMoviePlayer()->play(name, stopMusic);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Force_Facing(EMCState *state) {
+ _vm->getDrew()->setFacing(stackPos(0));
+ _vm->getDrew()->playStandingAnim();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Restart_Thread(EMCState *state) {
+
+ int32 sceneId = stackPos(0);
+ _vm->getScript()->init(&_vm->getSceneAnimationScript(sceneId)->_state, _vm->getSceneAnimationScript(sceneId)->_data);
+ _vm->getScript()->start(&_vm->getSceneAnimationScript(sceneId)->_state, 9 + sceneId);
+
+ if (!stackPos(1))
+ _vm->setSceneAnimationScriptUpdate(false);
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Walk_Actor_To_Point(EMCState *state) {
+ return _vm->getDrew()->walkTo(stackPos(0), stackPos(1));
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Sack_Visible(EMCState *state) {
+ _vm->state()->_sackVisible = stackPos(0) > 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Actor_Facing(EMCState *state) {
+ _vm->getDrew()->setFacing(stackPos(0));
+ _vm->getDrew()->playStandingAnim();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Confiscate_Inventory(EMCState *state) {
+ for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) {
+ _vm->state()->_confiscatedInventory[_vm->state()->_numConfiscatedInventoryItems] = _vm->state()->_inventory[i];
+ _vm->state()->_numConfiscatedInventoryItems++;
+ }
+ _vm->state()->_numInventoryItems = 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Character_Talks(EMCState *state) {
+ _vm->characterTalk(stackPos(0), false);
+ //_vm->characterTalk(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Visited_Scene(EMCState *state) {
+ return _vm->state()->_locations[stackPos(0)]._visited ? 1 : 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Rif_Flag(EMCState *state) {
+
+ int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1));
+ if (hs >= 0)
+ return _vm->getHotspots()->Get(hs)->getData(stackPos(2));
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Scroll(EMCState *state) {
+ return _vm->state()->_currentScrollValue;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Initial_Location(EMCState *state) {
+ int32 initialLocation = stackPos(0);
+ _vm->state()->_currentScene = initialLocation;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Make_Line_Non_Walkable(EMCState *state) {
+ _vm->makeLineNonWalkable(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ // we have to store some info for savegame
+ _vm->getSaveBufferStream()->writeSint16BE(2); // 2 = sys_Cmd_Make_Line_Non_Walkable
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(0));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(1));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(2));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Make_Line_Walkable(EMCState *state) {
+ _vm->makeLineWalkable(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ // we have to store some info for savegame
+ _vm->getSaveBufferStream()->writeSint16BE(3); // 3 = sys_Cmd_Make_Line_Walkable
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(0));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(1));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(2));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Walk_Actor_On_Condition(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Actor_Facing_Point(EMCState *state) {
+ int32 fx = stackPos(0);
+ int32 fy = stackPos(1);
+ _vm->getDrew()->setFacing(_vm->getDrew()->getFacingFromDirection(fx - _vm->getDrew()->getX(), fy - _vm->getDrew()->getY()));
+ return 1;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Inventory_Slot(EMCState *state) {
+ _vm->state()->_inventory[stackPos(1)] = stackPos(0);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Inventory_Slot(EMCState *state) {
+ return _vm->state()->_inventory[stackPos(0)];
+}
+
+int32 ScriptFunc::sys_Cmd_Add_Item_To_Inventory(EMCState *state) {
+ _vm->addItemToInventory(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Actor_RGB_Modifiers(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Init_Conversation_AP(EMCState *state) {
+ debugC(0, 0xfff, "init_conversation_ap %d %d %d %d", stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _vm->initCharacter(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Actor_Talks(EMCState *state) {
+ _vm->characterTalk(stackPos(0), false);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Say_Lines(EMCState *state) {
+ //_vm->sayLines(2, 1440);
+ _vm->sayLines(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Rif_Flag(EMCState *state) {
+ int32 hs = _vm->getHotspots()->FindBasedOnCorner(stackPos(0), stackPos(1));
+ if (hs >= 0)
+ _vm->getHotspots()->Get(hs)->setData(stackPos(2), stackPos(3));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Empty_Inventory(EMCState *state) {
+
+ for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++)
+ _vm->state()->_inventory[i] = 0;
+
+ _vm->state()->_numInventoryItems = 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Anim_Scale_Size(EMCState *state) {
+ return 0;
+}
+int32 ScriptFunc::sys_Cmd_Delete_Item_From_Inventory(EMCState *state) {
+ for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) {
+ if (stackPos(0) == _vm->state()->_inventory[i])
+ _vm->state()->_inventory[i] = 0;
+ }
+ _vm->rearrangeInventory();
+ return 0;
+}
+int32 ScriptFunc::sys_Cmd_Specific_Item_In_Inventory(EMCState *state) {
+ for (int32 i = 0; i < _vm->state()->_numInventoryItems; i++) {
+ if (_vm->state()->_inventory[i] == stackPos(0))
+ return 1;
+ }
+ if (_vm->state()->_mouseState == stackPos(0))
+ return 1;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Run_Script(EMCState *state) {
+ return _vm->runEventScript(_vm->getMouseX(), _vm->getMouseY(), 2, stackPos(0), 0);
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Game_Flag(EMCState *state) {
+ int32 arg = stackPos(0);
+ return (_vm->state()->_gameFlag[arg >> 3] & (1 << (arg & 7))) != 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Reset_Game_Flag(EMCState *state) {
+ int32 arg = stackPos(0);
+ _vm->state()->_gameFlag[arg >> 3] &= ~(1 << (arg & 7));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Game_Flag(EMCState *state) {
+ int32 arg = stackPos(0);
+ _vm->state()->_gameFlag[arg >> 3] |= (1 << (arg & 7));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Create_Mouse_Item(EMCState *state) {
+ _vm->createMouseItem(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Destroy_Mouse_Item(EMCState *state) {
+ _vm->deleteMouseItem();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Mouse_State(EMCState *state) {
+ return _vm->state()->_mouseState;
+}
+
+int32 ScriptFunc::sys_Cmd_Hide_Mouse(EMCState *state) {
+ _vm->state()->_mouseHidden = true;
+ //if (Game.MouseHiddenCount > 0) Game.MouseHiddenCount = 1;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Exit_Conversation(EMCState *state) {
+ _vm->state()->_exitConversation = true;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Mouse_Pos(EMCState *state) {
+ _vm->getSystem()->warpMouse(stackPos(0) - _vm->state()->_currentScrollValue, stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Show_Mouse(EMCState *state) {
+ _vm->state()->_mouseHidden = false;
+ //if (Game.MouseHiddenCount < 0) Game.MouseHiddenCount = 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_In_Close_Up(EMCState *state) {
+ return _vm->state()->_inCloseUp;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Scroll_Lock(EMCState *state) {
+ _vm->state()->_currentScrollLock = stackPos(0) > 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Fill_Area_Non_Walkable(EMCState *state) {
+ _vm->getMask()->floodFillNotWalkableOnMask(stackPos(0), stackPos(1));
+
+ // we have to store some info for savegame
+ _vm->getSaveBufferStream()->writeSint16BE(4); // 4 = sys_Cmd_Make_Line_Walkable
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(0));
+ _vm->getSaveBufferStream()->writeSint16BE(stackPos(1));
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Scroll_Coords(EMCState *state) {
+ _vm->state()->_currentScrollValue = stackPos(0);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Hide_Cutaway(EMCState *state) {
+ _vm->hideCutaway();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Show_Cutaway(EMCState *state) {
+ _vm->showCutaway("");
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Pause_Ticks(EMCState *state) {
+
+ if (!_vm->isUpdatingSceneAnimation() || _vm->getScriptRegionNested() > 0) {
+ if (stackPos(1))
+ _vm->waitTicks(stackPos(0), true);
+ else
+ _vm->waitTicks(stackPos(0), false);
+ } else {
+ uint32 sceneId = _vm->getCurrentUpdatingSceneAnimation();
+ uint32 waitTicks = stackPos(0);
+ if (waitTicks < 1) waitTicks = 1;
+
+ waitTicks *= _vm->getTickLength();
+
+ if (sceneId < 40) {
+ int32 nextTicks = waitTicks + _vm->getSceneAnimationScript(sceneId)->_lastTimer;
+ if (nextTicks < _vm->getOldMilli())
+ _vm->getSceneAnimationScript(sceneId)->_lastTimer = _vm->getOldMilli() + waitTicks;
+ else
+ _vm->getSceneAnimationScript(sceneId)->_lastTimer = nextTicks;
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_In_Conversation(EMCState *state) {
+ return _vm->state()->_inConversation;
+}
+
+int32 ScriptFunc::sys_Cmd_Character_Talking(EMCState *state) {
+ int32 characterId = stackPos(0);
+ Character *character = _vm->getCharacterById(characterId);
+ if (character)
+ return (character->getFlag() & 4) && (character->getFlag() & 8);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Flux_Facing_Point(EMCState *state) {
+ int32 fx = stackPos(0);
+ int32 fy = stackPos(1);
+ _vm->getFlux()->setFacing(_vm->getFlux()->getFacingFromDirection(fx - _vm->getFlux()->getX(), fy - _vm->getFlux()->getY()));
+ _vm->getFlux()->playStandingAnim();
+ return 1;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Flux_Facing(EMCState *state) {
+ _vm->getFlux()->setFacing(stackPos(0));
+ _vm->getFlux()->playStandingAnim();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Flux_Coords(EMCState *state) {
+ _vm->getFlux()->stopWalk();
+ _vm->getFlux()->setPosition(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Flux_Visible(EMCState *state) {
+ _vm->getFlux()->setVisible(stackPos(0) > 0);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Flux_X(EMCState *state) {
+ return _vm->getFlux()->getX();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Flux_Y(EMCState *state) {
+ return _vm->getFlux()->getY();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Flux_Facing(EMCState *state) {
+ return _vm->getFlux()->getFacing();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Flux_Flags(EMCState *state) {
+ return (_vm->getFlux()->getFlag() & stackPos(0)) != 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Flux_Coords(EMCState *state) {
+ return (stackPos(0) == _vm->getFlux()->getX()) && (stackPos(1) == _vm->getFlux()->getY());
+}
+
+int32 ScriptFunc::sys_Cmd_Have_A_Conversation(EMCState *state) {
+ _vm->haveAConversation(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Walk_Flux_To_Point(EMCState *state) {
+ _vm->getFlux()->walkTo(stackPos(0), stackPos(1));
+ return 1;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Scene_Anim_Loaded(EMCState *state) {
+ return _vm->getSceneAnimation(stackPos(0))->_active;
+}
+
+int32 ScriptFunc::sys_Cmd_Play_Flux_Anim(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Anim_Priority(EMCState *state) {
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(stackPos(0));
+ sceneAnim->_animInstance->setLayerZ(stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Place_Scene_Anim(EMCState *state) {
+ int32 sceneId = stackPos(0);
+ int32 x = stackPos(1);
+ int32 y = stackPos(2);
+ int32 frame = stackPos(5);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId);
+ sceneAnim->_animInstance->setPosition(x, y, 0, false);
+ sceneAnim->_animInstance->forceFrame(frame);
+ _vm->setSceneAnimationScriptUpdate(false);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Update_Scene_Animations(EMCState *state) {
+ //debugC(0, 0xfff, "UpdateAnimations");
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Drew_Scale(EMCState *state) {
+ int32 scale = _vm->getDrew()->getScale();
+ if (!scale)
+ return 1024;
+ return scale;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Drew_Flags(EMCState *state) {
+ return (_vm->getDrew()->getFlag() & stackPos(0)) != 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Music(EMCState *state) {
+
+ char *newMus = GetText(0, state);
+ _vm->getAudioManager()->playMusic(_vm->state()->_locations[_vm->state()->_currentScene]._name, newMus);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_Speech(EMCState *state) {
+ if (_vm->getAudioManager()->voiceStillPlaying())
+ return 1;
+ else
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Enter_New_Scene(EMCState *state) {
+ _vm->exitScene();
+ _vm->getDrew()->setFacing(stackPos(1));
+ _vm->loadScene(stackPos(0));
+ _vm->setSceneAnimationScriptUpdate(false);
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Enter_Same_Scene(EMCState *state) {
+ _vm->exitScene();
+ _vm->loadScene(_vm->state()->_currentScene);
+ _vm->setSceneAnimationScriptUpdate(false);
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Is_Pixel_Walkable(EMCState *state) {
+ return (_vm->getMask()->getData(stackPos(0), stackPos(1)) & 0x1f) > 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Show_Screen(EMCState *state) {
+ _vm->showCutaway(GetText(0, state));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Hide_Screen(EMCState *state) {
+ _vm->hideCutaway();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Special_Enter_X_And_Y(EMCState *state) {
+ _vm->state()->_nextSpecialEnterX = stackPos(0);
+ _vm->state()->_nextSpecialEnterY = stackPos(1);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Mouse_X(EMCState *state) {
+ return _vm->getMouseX();
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Mouse_Y(EMCState *state) {
+ return _vm->getMouseY();
+}
+
+int32 ScriptFunc::sys_Cmd_Fade_Palette(EMCState *state) {
+ debugC(0, 0xfff, "fadePalette %d %d", stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Music_Enabled(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Random(EMCState *state) {
+ int32 randMin = stackPos(0);
+ int32 randMax = stackPos(1);
+ int32 t = 0;
+
+ if (randMin > randMax) {
+ t = randMin;
+ randMin = randMax;
+ randMax = t;
+ }
+ return _vm->randRange(randMin, randMax);
+}
+
+int32 ScriptFunc::sys_Cmd_Wait_Key(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back(EMCState *state) {
+ // draw the frame in the backbuffer (picture then)
+
+ int32 animId = stackPos(0);
+ int32 frame = stackPos(1);
+
+ if (frame < 0)
+ return 0;
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
+
+ if (sceneAnim->_active) {
+ sceneAnim->_animInstance->setFrame(frame);
+ sceneAnim->_animInstance->setAnimationRange(frame, frame);
+ sceneAnim->_animInstance->stopAnimation();
+ sceneAnim->_animInstance->renderOnPicture();
+
+ // we have to store some info for savegame
+ _vm->getSaveBufferStream()->writeSint16BE(1); // 1 = Draw_Scene_Anim_WSA_Frame_To_Back
+ _vm->getSaveBufferStream()->writeSint16BE(frame);
+ _vm->getSaveBufferStream()->writeSint16BE(strlen(sceneAnim->_animInstance->getAnimation()->_name) + 1);
+ _vm->getSaveBufferStream()->write(sceneAnim->_animInstance->getAnimation()->_name, strlen(sceneAnim->_animInstance->getAnimation()->_name) + 1);
+ _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getX());
+ _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getY());
+ _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getZ());
+ _vm->getSaveBufferStream()->writeSint16BE(sceneAnim->_animInstance->getLayerZ());
+
+ }
+ return 1;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Scene_Anim_Wait(EMCState *state) {
+ int32 sceneId = stackPos(0);
+ int32 waitTicks = stackPos(1);
+ if (waitTicks < 1) waitTicks = 1;
+
+ // WORKAROUND : To fix the timing problem in the Gift-O-Matic
+ // The animation was too fast and the player was unable to click fast enough to get objects
+ // Here we slow down the animation
+ if (_vm->state()->_currentScene == 24) {
+ if (_vm->getCurrentUpdatingSceneAnimation() == 6) {
+ if (waitTicks == 1) {
+ waitTicks = 10;
+ _vm->setSceneAnimationScriptUpdate(false);
+ }
+ }
+ }
+
+ // WORKAROUND : In Wolf place, the animation to move the pot was too fast and the player was unable to
+ // progress into the game.
+ if (_vm->state()->_currentScene == 29) {
+ if (_vm->getCurrentUpdatingSceneAnimation() == 8 || _vm->getCurrentUpdatingSceneAnimation() == 7) {
+ if (waitTicks == 1) {
+ waitTicks = 5;
+ _vm->setSceneAnimationScriptUpdate(false);
+ }
+ }
+ }
+
+ // WORKAROUND : In transformed hangar, everything is too fast..
+ if (_vm->state()->_currentScene == 19) {
+ waitTicks = 10;
+ _vm->setSceneAnimationScriptUpdate(false);
+ }
+
+ waitTicks *= _vm->getTickLength();
+
+ if (sceneId >= 0 && sceneId < 40) {
+ int32 nextTicks = waitTicks + _vm->getSceneAnimationScript(sceneId)->_lastTimer;
+ //debugC(0,0xff, "sw : assigining %d to lasttimer of %d (current tick %d old milli %d) ",nextTicks, sceneId , _vm->getSystem()->getMillis(), _vm->getOldMilli());
+ if (nextTicks < _vm->getOldMilli())
+ _vm->getSceneAnimationScript(sceneId)->_lastTimer = _vm->getOldMilli() + waitTicks;
+ else
+ _vm->getSceneAnimationScript(sceneId)->_lastTimer = nextTicks;
+ }
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Init_Scene_Anim(EMCState *state) {
+ int32 animId = stackPos(0);
+ int32 flags = stackPos(1);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
+ if (sceneAnim->_active)
+ return 0;
+
+ sceneAnim->_animation = new Animation(_vm);
+ sceneAnim->_animation->loadAnimation(GetText(12, state));
+ sceneAnim->_animInstance = _vm->getAnimationManager()->createNewInstance(kAnimationScene);
+ sceneAnim->_animInstance->setAnimation(sceneAnim->_animation);
+ sceneAnim->_animInstance->setVisible((flags & 1) != 0);
+ sceneAnim->_animInstance->setAnimationRange(stackPos(11), stackPos(11));
+ sceneAnim->_animInstance->setFrame(stackPos(11));
+
+ _vm->getAnimationManager()->addInstance(sceneAnim->_animInstance);
+
+ debugC(0, 0xfff, "Init Anim %s %d %d %d %d %d %d %d %d %d %d %d %d %d\n", GetText(12, state), stackPos(0), stackPos(1), stackPos(2), stackPos(3),
+ stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9), stackPos(10), stackPos(11), stackPos(12));
+
+ int32 dx = stackPos(4);
+ int32 dy = stackPos(5);
+ int32 layerZ = stackPos(3);
+
+ if (dx == -2)
+ sceneAnim->_animInstance->moveRelative(640, 0, 0);
+ else if (dx >= 0)
+ sceneAnim->_animInstance->setX(dx);
+
+ if (dy >= 0)
+ sceneAnim->_animInstance->setY(dy);
+ else
+ dy = sceneAnim->_animation->_y1;
+
+ if (flags & 0x20)
+ sceneAnim->_animInstance->setZ(_vm->getLayerAtPoint(dx, dy));
+
+ if (layerZ >= 0) {
+ sceneAnim->_animInstance->setLayerZ(layerZ);
+ } else {
+ dy = dy + sceneAnim->_animation->_y2 - sceneAnim->_animation->_y1 - 1;
+ sceneAnim->_animInstance->setLayerZ(dy);
+ }
+
+ sceneAnim->_animInstance->setId(stackPos(0));
+
+
+ sceneAnim->_active = true;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Scene_Animation_Active_Flag(EMCState *state) {
+ int32 animId = stackPos(0);
+ int32 activeFlag = stackPos(1);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
+
+ if (sceneAnim->_active)
+ sceneAnim->_animInstance->setVisible(activeFlag > 0);
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Draw_Scene_Anim_WSA_Frame(EMCState *state) {
+ int32 animId = stackPos(0);
+ int32 frame = stackPos(1);
+
+ if (frame < 0)
+ return 0;
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
+
+ if (sceneAnim->_active) {
+ sceneAnim->_animInstance->setFrame(frame);
+ sceneAnim->_animInstance->setAnimationRange(frame, frame);
+ sceneAnim->_animInstance->stopAnimation();
+ }
+ _vm->setSceneAnimationScriptUpdate(false);
+
+ // WORKAROUND : Too fast animations...
+ if (_vm->state()->_currentScene == 26 && animId == 22)
+ _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 3);
+
+ if (_vm->state()->_currentScene == 14) {
+ if (animId == 3 || animId == 2 || animId == 4)
+ _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 2);
+ else if (animId == 20 || animId == 15 || animId == 21 || animId == 16 || animId == 17 || animId == 18)
+ _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 1);
+ else if (animId == 9) {
+ _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 6);
+ }
+ }
+
+ if (_vm->state()->_currentScene == 29) {
+ if (animId == 16 || animId == 26 || animId == 36)
+ _vm->pauseSceneAnimationScript(_vm->getCurrentUpdatingSceneAnimation(), 2);
+ }
+
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Move_Scene_Anim(EMCState *state) {
+ int32 animId = stackPos(0);
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(animId);
+ sceneAnim->_animInstance->moveRelative(stackPos(1), stackPos(2), 0);
+ _vm->setSceneAnimationScriptUpdate(false);
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Run_Actor_Default_Script(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Location_Data(EMCState *state) {
+ // initial setup of locations
+ int32 locationId = stackPos(0);
+ debugC(0, 0, "setlocationdata(%d) %s %x %s %s %d %d", locationId, GetText(1, state), stackPos(2), GetText(3, state), GetText(4, state), stackPos(5), stackPos(6));
+ strcpy(_vm->state()->_locations[locationId]._name, GetText(1, state));
+ strcpy(_vm->state()->_locations[locationId]._music, GetText(3, state));
+ strcpy(_vm->state()->_locations[locationId]._cutaway, GetText(4, state));
+ _vm->state()->_locations[locationId]._flags = stackPos(2);
+ _vm->state()->_locations[locationId]._visited = false;
+ _vm->state()->_locations[locationId]._numSceneAnimations = stackPos(5);
+
+ return 0;
+
+}
+
+int32 ScriptFunc::sys_Cmd_Set_CountDown_Timer(EMCState *state) {
+ // game timer is in ticks
+ _vm->state()->_gameTimer = stackPos(0) * _vm->getTickLength();
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Query_CountDown_Timer(EMCState *state) {
+ return _vm->state()->_gameTimer;
+}
+
+int32 ScriptFunc::sys_Cmd_Proceed_To_Next_Chapter(EMCState *state) {
+
+ _vm->state()->_currentChapter = stackPos(0);
+ _vm->exitScene();
+ _vm->loadScene(stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Play_Sfx_Plus(EMCState *state) {
+ //debugC(0,0xfff, "playSfx ( %d , %d, %d, %d, %d )", stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _vm->playSFX(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Play_Sfx(EMCState *state) {
+ //debugC(0,0xfff, "playSfx ( %d , %d)", stackPos(0), stackPos(1));
+ _vm->playSFX(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Kill_Ambient_Sfx(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Ambient_Sfx_Plus(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Ambient_Volume(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Freeze_Scene_Animation(EMCState *state) {
+
+ _vm->getSceneAnimationScript(stackPos(0))->_frozen = true;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Unfreeze_Scene_Animation(EMCState *state) {
+ _vm->getSceneAnimationScript(stackPos(0))->_frozen = false;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Scene_Animation_Frozen(EMCState *state) {
+ return _vm->getSceneAnimationScript(stackPos(0))->_frozen ? 1 : 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Script_Game_Data_Global(EMCState *state) {
+ _vm->state()->_gameGlobalData[stackPos(0)] = stackPos(1);
+ return stackPos(1);
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Script_Game_Data_Global(EMCState *state) {
+ return _vm->state()->_gameGlobalData[stackPos(0)];
+}
+
+int32 ScriptFunc::sys_Cmd_Say_Line(EMCState *state) {
+ _vm->sayLines(1 , stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Knight_Puzzle_Get_Coord(EMCState *state) {
+ static const uint16 knightCoords[] = {
+ 0x327, 0x0fa, 0x354, 0x0f8, 0x383, 0x0f4, 0x3a4, 0x0f0, 0x3d3, 0x0ed,
+ 0x3fd, 0x0ef, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x310, 0x109, 0x349, 0x103,
+ 0x378, 0x103, 0x3a4, 0x102, 0x3d5, 0x102, 0x403, 0x103, 0x2fe, 0x12d,
+ 0x2fe, 0x12d, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x369, 0x123, 0x3a4, 0x123,
+ 0x3d8, 0x121, 0x410, 0x124, 0x2fe, 0x12d, 0x2fe, 0x12d, 0x2fe, 0x12d,
+ 0x2fe, 0x12d, 0x35d, 0x14f, 0x3a2, 0x149, 0x3db, 0x149, 0x415, 0x14a,
+ 0x2fe, 0x12d, 0x2fe, 0x12d
+ };
+
+ return knightCoords[stackPos(2) + 2 * (8 * stackPos(1) + stackPos(0))];
+}
+
+int32 ScriptFunc::sys_Cmd_Add_Scene_Anim(EMCState *state) {
+ return sys_Cmd_Init_Scene_Anim(state);
+}
+
+int32 ScriptFunc::sys_Cmd_Remove_Scene_Anim(EMCState *state) {
+ int32 sceneId = stackPos(0);
+
+ if (!_vm->getSceneAnimation(sceneId)->_active)
+ return 0;
+
+ SceneAnimation *sceneAnim = _vm->getSceneAnimation(sceneId);
+ sceneAnim->_active = false;
+ _vm->getAnimationManager()->removeInstance(sceneAnim->_animInstance);
+ sceneAnim->_animation = 0;
+ sceneAnim->_animInstance = 0;
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Disable_Timer(EMCState *state) {
+ _vm->disableTimer(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Enable_Timer(EMCState *state) {
+ _vm->enableTimer(stackPos(0));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Timer(EMCState *state) {
+ _vm->setTimer(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Set_Palette_Color(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Number_Of_NPCs(EMCState *state) {
+ return 0;
+}
+
+int32 ScriptFunc::sys_Cmd_Get_Config_Language(EMCState *state) {
+ return 0;
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/script_func.h b/engines/toon/script_func.h
new file mode 100644
index 0000000000..2f8972134c
--- /dev/null
+++ b/engines/toon/script_func.h
@@ -0,0 +1,171 @@
+/* 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 SCRIPT_FUNC_H
+#define SCRIPT_FUNC_H
+
+#include "common/array.h"
+#include "toon/script.h"
+
+namespace Toon {
+
+
+class ScriptFunc {
+public:
+ ScriptFunc(ToonEngine *vm);
+ ~ScriptFunc(void);
+ Common::Array<const Opcode *> _opcodes;
+ ToonEngine *_vm;
+
+#define SYSFUNC(x) int32 x(EMCState*)
+ SYSFUNC(sys_Cmd_Dummy);
+ SYSFUNC(sys_Cmd_Change_Actor_X_And_Y);
+ SYSFUNC(sys_Cmd_Init_Talking_Character);
+ SYSFUNC(sys_Cmd_Draw_Actor_Standing);
+ SYSFUNC(sys_Cmd_Get_Actor_X);
+ SYSFUNC(sys_Cmd_Get_Actor_Y);
+ SYSFUNC(sys_Cmd_Get_Actor_Facing);
+ SYSFUNC(sys_Cmd_Get_Last_Scene);
+ SYSFUNC(sys_Cmd_Debug_Print);
+ SYSFUNC(sys_Cmd_Flip_Screens);
+ SYSFUNC(sys_Cmd_Play_Flic);
+ SYSFUNC(sys_Cmd_Force_Facing);
+ SYSFUNC(sys_Cmd_Restart_Thread);
+ SYSFUNC(sys_Cmd_Walk_Actor_To_Point);
+ SYSFUNC(sys_Cmd_Set_Sack_Visible);
+ SYSFUNC(sys_Cmd_Set_Actor_Facing);
+ SYSFUNC(sys_Cmd_Confiscate_Inventory);
+ SYSFUNC(sys_Cmd_Character_Talks);
+ SYSFUNC(sys_Cmd_Visited_Scene);
+ SYSFUNC(sys_Cmd_Query_Rif_Flag);
+ SYSFUNC(sys_Cmd_Query_Scroll);
+ SYSFUNC(sys_Cmd_Set_Initial_Location);
+ SYSFUNC(sys_Cmd_Make_Line_Non_Walkable);
+ SYSFUNC(sys_Cmd_Make_Line_Walkable);
+ SYSFUNC(sys_Cmd_Walk_Actor_On_Condition);
+ SYSFUNC(sys_Cmd_Set_Actor_Facing_Point);
+ SYSFUNC(sys_Cmd_Set_Inventory_Slot);
+ SYSFUNC(sys_Cmd_Get_Inventory_Slot);
+ SYSFUNC(sys_Cmd_Add_Item_To_Inventory);
+ SYSFUNC(sys_Cmd_Set_Actor_RGB_Modifiers);
+ SYSFUNC(sys_Cmd_Init_Conversation_AP);
+ SYSFUNC(sys_Cmd_Actor_Talks);
+ SYSFUNC(sys_Cmd_Say_Lines);
+ SYSFUNC(sys_Cmd_Set_Rif_Flag);
+ SYSFUNC(sys_Cmd_Empty_Inventory);
+ SYSFUNC(sys_Cmd_Set_Anim_Scale_Size);
+ SYSFUNC(sys_Cmd_Delete_Item_From_Inventory);
+ SYSFUNC(sys_Cmd_Specific_Item_In_Inventory);
+ SYSFUNC(sys_Cmd_Run_Script);
+ SYSFUNC(sys_Cmd_Query_Game_Flag);
+ SYSFUNC(sys_Cmd_Reset_Game_Flag);
+ SYSFUNC(sys_Cmd_Set_Game_Flag);
+ SYSFUNC(sys_Cmd_Create_Mouse_Item);
+ SYSFUNC(sys_Cmd_Destroy_Mouse_Item);
+ SYSFUNC(sys_Cmd_Get_Mouse_State);
+ SYSFUNC(sys_Cmd_Hide_Mouse);
+ SYSFUNC(sys_Cmd_Exit_Conversation);
+ SYSFUNC(sys_Cmd_Set_Mouse_Pos);
+ SYSFUNC(sys_Cmd_Show_Mouse);
+ SYSFUNC(sys_Cmd_In_Close_Up);
+ SYSFUNC(sys_Cmd_Set_Scroll_Lock);
+ SYSFUNC(sys_Cmd_Fill_Area_Non_Walkable);
+ SYSFUNC(sys_Cmd_Set_Scroll_Coords);
+ SYSFUNC(sys_Cmd_Hide_Cutaway);
+ SYSFUNC(sys_Cmd_Show_Cutaway);
+ SYSFUNC(sys_Cmd_Pause_Ticks);
+ SYSFUNC(sys_Cmd_In_Conversation);
+ SYSFUNC(sys_Cmd_Character_Talking);
+ SYSFUNC(sys_Cmd_Set_Flux_Facing_Point);
+ SYSFUNC(sys_Cmd_Set_Flux_Facing);
+ SYSFUNC(sys_Cmd_Set_Flux_Coords);
+ SYSFUNC(sys_Cmd_Set_Flux_Visible);
+ SYSFUNC(sys_Cmd_Get_Flux_X);
+ SYSFUNC(sys_Cmd_Get_Flux_Y);
+ SYSFUNC(sys_Cmd_Get_Flux_Facing);
+ SYSFUNC(sys_Cmd_Get_Flux_Flags);
+ SYSFUNC(sys_Cmd_Query_Flux_Coords);
+ SYSFUNC(sys_Cmd_Have_A_Conversation);
+ SYSFUNC(sys_Cmd_Walk_Flux_To_Point);
+ SYSFUNC(sys_Cmd_Query_Scene_Anim_Loaded);
+ SYSFUNC(sys_Cmd_Play_Flux_Anim);
+ SYSFUNC(sys_Cmd_Set_Anim_Priority);
+ SYSFUNC(sys_Cmd_Place_Scene_Anim);
+ SYSFUNC(sys_Cmd_Update_Scene_Animations);
+ SYSFUNC(sys_Cmd_Get_Drew_Scale);
+ SYSFUNC(sys_Cmd_Query_Drew_Flags);
+ SYSFUNC(sys_Cmd_Set_Music);
+ SYSFUNC(sys_Cmd_Query_Speech);
+ SYSFUNC(sys_Cmd_Enter_New_Scene);
+ SYSFUNC(sys_Cmd_Enter_Same_Scene);
+ SYSFUNC(sys_Cmd_Is_Pixel_Walkable);
+ SYSFUNC(sys_Cmd_Show_Screen);
+ SYSFUNC(sys_Cmd_Hide_Screen);
+ SYSFUNC(sys_Cmd_Set_Special_Enter_X_And_Y);
+ SYSFUNC(sys_Cmd_Get_Mouse_X);
+ SYSFUNC(sys_Cmd_Get_Mouse_Y);
+ SYSFUNC(sys_Cmd_Fade_Palette);
+ SYSFUNC(sys_Cmd_Music_Enabled);
+ SYSFUNC(sys_Cmd_Random);
+ SYSFUNC(sys_Cmd_Wait_Key);
+ SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back);
+ SYSFUNC(sys_Cmd_Set_Scene_Anim_Wait);
+ SYSFUNC(sys_Cmd_Init_Scene_Anim);
+ SYSFUNC(sys_Cmd_Set_Scene_Animation_Active_Flag);
+ SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame);
+ SYSFUNC(sys_Cmd_Move_Scene_Anim);
+ SYSFUNC(sys_Cmd_Run_Actor_Default_Script);
+ SYSFUNC(sys_Cmd_Set_Location_Data);
+ SYSFUNC(sys_Cmd_Set_CountDown_Timer);
+ SYSFUNC(sys_Cmd_Query_CountDown_Timer);
+ SYSFUNC(sys_Cmd_Proceed_To_Next_Chapter);
+ SYSFUNC(sys_Cmd_Play_Sfx_Plus);
+ SYSFUNC(sys_Cmd_Play_Sfx);
+ SYSFUNC(sys_Cmd_Set_Ambient_Sfx);
+ SYSFUNC(sys_Cmd_Kill_Ambient_Sfx);
+ SYSFUNC(sys_Cmd_Set_Ambient_Sfx_Plus);
+ SYSFUNC(sys_Cmd_Set_Ambient_Volume);
+ SYSFUNC(sys_Cmd_Freeze_Scene_Animation);
+ SYSFUNC(sys_Cmd_Unfreeze_Scene_Animation);
+ SYSFUNC(sys_Cmd_Scene_Animation_Frozen);
+ SYSFUNC(sys_Cmd_Set_Script_Game_Data_Global);
+ SYSFUNC(sys_Cmd_Get_Script_Game_Data_Global);
+ SYSFUNC(sys_Cmd_Say_Line);
+ SYSFUNC(sys_Cmd_Knight_Puzzle_Get_Coord);
+ SYSFUNC(sys_Cmd_Add_Scene_Anim);
+ SYSFUNC(sys_Cmd_Remove_Scene_Anim);
+ SYSFUNC(sys_Cmd_Disable_Timer);
+ SYSFUNC(sys_Cmd_Enable_Timer);
+ SYSFUNC(sys_Cmd_Set_Timer);
+ SYSFUNC(sys_Cmd_Set_Palette_Color);
+ SYSFUNC(sys_Cmd_Number_Of_NPCs);
+ SYSFUNC(sys_Cmd_Get_Config_Language);
+ SYSFUNC(sys_Cmd_Get_Actor_Final_X);
+ SYSFUNC(sys_Cmd_Get_Actor_Final_Y);
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/state.cpp b/engines/toon/state.cpp
new file mode 100644
index 0000000000..71674688d5
--- /dev/null
+++ b/engines/toon/state.cpp
@@ -0,0 +1,261 @@
+/* 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 "toon/state.h"
+#include "toon/toon.h"
+
+namespace Toon {
+
+void Location::save(Common::WriteStream *stream) {
+ stream->write(_cutaway, 64);
+ stream->write(_music, 64);
+ stream->write(_name, 64);
+ stream->writeSint16BE(_numRifBoxes);
+ stream->writeSint16BE(_numSceneAnimations);
+ stream->writeSByte(_visited);
+
+ for (int32 i = 0; i < _numRifBoxes * 2; i++) {
+ stream->writeSint16BE(_rifBoxesFlags[i]);
+ }
+}
+void Location::load(Common::ReadStream *stream) {
+ stream->read(_cutaway, 64);
+ stream->read(_music, 64);
+ stream->read(_name, 64);
+ _numRifBoxes = stream->readSint16BE();
+ _numSceneAnimations = stream->readSint16BE();
+ _visited = stream->readSByte();
+
+ for (int32 i = 0; i < _numRifBoxes * 2; i++) {
+ _rifBoxesFlags[i] = stream->readSint16BE();
+ }
+}
+
+State::State(void) {
+ for (int32 i = 0; i < 64; i++) {
+ _locations[i]._visited = false;
+ _locations[i]._numSceneAnimations = 0;
+ _locations[i]._numRifBoxes = 0;
+ }
+
+ memset(_gameFlag, 0, sizeof(_gameFlag));
+ memset(_gameGlobalData, -1, sizeof(_gameGlobalData));
+
+ for (int32 i = 0; i < 2; i++) {
+ _timerEnabled[i] = false;
+ _timerTimeout[i] = 0;
+ _timerDelay[i] = -1;
+ }
+
+ _lastVisitedScene = -1;
+ _currentScene = -1;
+
+ _currentScrollLock = false;
+ _currentScrollValue = 0;
+
+ _gameTimer = 0;
+ _currentChapter = 1;
+
+ _showConversationIcons = false;
+
+ _inCloseUp = false;
+ _inConversation = false;
+
+ _mouseState = -1;
+ _mouseHidden = false;
+
+ _firstConverstationLine = false;
+
+ _sackVisible = false; // to change
+ _inCutaway = false;
+
+ _inInventory = false;
+ _numInventoryItems = 0; //To chhange
+ _numConfiscatedInventoryItems = 0;
+
+ _nextSpecialEnterX = -1;
+ _nextSpecialEnterY = -1;
+
+#if 0
+ for (int i = 0; i < 30; i++) {
+ _inventory[i] = 90 + i;
+ if (_inventory[i] == 41)
+ _inventory[i] = 42;
+ }
+
+ _inventory[0] = 53;
+ _inventory[1] = 22;
+ _inventory[2] = 93;
+ _inventory[3] = 49;
+ _inventory[4] = 47;
+ _inventory[5] = 14;
+ _numInventoryItems = 6; //To change
+#endif
+
+ memset(_conversationState, 0, sizeof(Conversation) * 60);
+}
+
+State::~State(void) {
+
+}
+
+int32 State::getGameFlag(int32 flagId) {
+ return (_gameFlag[flagId >> 3] & (1 << (flagId & 7))) != 0;
+}
+
+bool State::hasItemInInventory(int32 item) {
+ debugC(1, kDebugState, "hasItemInInventory(%d)", item);
+
+ for (int32 i = 0; i < _numInventoryItems; i++) {
+ if (_inventory[i] == item)
+ return true;
+ }
+ return false;
+}
+
+void State::save(Common::WriteStream *stream) {
+
+ for (int32 i = 0; i < 256; i++) {
+ _locations[i].save(stream);
+ }
+
+ for (int32 i = 0; i < 256; i++) {
+ stream->writeSint16BE(_gameGlobalData[i]);
+ }
+
+ for (int32 i = 0; i < 256; i++) {
+ stream->writeSint16BE(_gameFlag[i]);
+ }
+
+ stream->writeSint16BE(_lastVisitedScene);
+ stream->writeSint16BE(_currentScene);
+ stream->writeSint16BE(_currentScrollValue);
+ stream->writeSByte(_currentScrollLock);
+
+ for (int32 i = 0; i < 35; i++) {
+ stream->writeSint16BE(_inventory[i]);
+ }
+
+ for (int32 i = 0; i < 35; i++) {
+ stream->writeSint16BE(_confiscatedInventory[i]);
+ }
+
+ stream->writeSint32BE(_numInventoryItems);
+ stream->writeSint32BE(_numConfiscatedInventoryItems);
+
+ stream->writeSByte(_inCloseUp);
+ stream->writeSByte(_inCutaway);
+ stream->writeSByte(_inConversation);
+ stream->writeSByte(_inInventory);
+ stream->writeSByte(_showConversationIcons);
+
+ stream->writeSint16BE(_mouseState);
+
+ stream->writeSint16BE(_currentConversationId);
+ stream->writeSByte(_firstConverstationLine);
+ stream->writeSByte(_exitConversation);
+ stream->writeSByte(_mouseHidden);
+ stream->writeSByte(_sackVisible);
+ stream->writeSint32BE(_gameTimer);
+ stream->writeSByte(_currentChapter);
+
+ stream->writeByte(_timerEnabled[0]);
+ stream->writeByte(_timerEnabled[1]);
+
+ stream->writeSint32BE(_timerTimeout[0]);
+ stream->writeSint32BE(_timerTimeout[1]);
+
+ stream->writeSint32BE(_timerDelay[0]);
+ stream->writeSint32BE(_timerDelay[1]);
+}
+
+void State::load(Common::ReadStream *stream) {
+ for (int32 i = 0; i < 256; i++) {
+ _locations[i].load(stream);
+ }
+
+ for (int32 i = 0; i < 256; i++) {
+ _gameGlobalData[i] = stream->readSint16BE();
+ }
+
+ for (int32 i = 0; i < 256; i++) {
+ _gameFlag[i] = stream->readSint16BE();
+ }
+
+ _lastVisitedScene = stream->readSint16BE();
+ _currentScene = stream->readSint16BE();
+ _currentScrollValue = stream->readSint16BE();
+ _currentScrollLock = stream->readSByte();
+
+ for (int32 i = 0; i < 35; i++) {
+ _inventory[i] = stream->readSint16BE();
+ }
+
+ for (int32 i = 0; i < 35; i++) {
+ _confiscatedInventory[i] = stream->readSint16BE();
+ }
+
+ _numInventoryItems = stream->readSint32BE();
+ _numConfiscatedInventoryItems = stream->readSint32BE();
+
+ _inCloseUp = stream->readSByte();
+ _inCutaway = stream->readSByte();
+ _inConversation = stream->readSByte();
+ _inInventory = stream->readSByte();
+ _showConversationIcons = stream->readSByte();
+
+ _mouseState = stream->readSint16BE();
+
+ _currentConversationId = stream->readSint16BE();
+ _firstConverstationLine = stream->readSByte();
+ _exitConversation = stream->readSByte();
+ _mouseHidden = stream->readSByte();
+ _sackVisible = stream->readSByte();
+ _gameTimer = stream->readSint32BE();
+ _currentChapter = stream->readSByte();
+
+ _timerEnabled[0] = stream->readByte();
+ _timerEnabled[1] = stream->readByte();
+
+ _timerTimeout[0] = stream->readSint32BE();
+ _timerTimeout[1] = stream->readSint32BE();
+
+ _timerDelay[0] = stream->readSint32BE();
+ _timerDelay[1] = stream->readSint32BE();
+}
+
+void State::loadConversations(Common::ReadStream *stream) {
+ for (int32 i = 0; i < 60; i++) {
+ _conversationState[i].load(stream, _conversationData);
+ }
+}
+
+void State::saveConversations(Common::WriteStream *stream) {
+ for (int32 i = 0; i < 60; i++) {
+ _conversationState[i].save(stream, _conversationData);
+ }
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/state.h b/engines/toon/state.h
new file mode 100644
index 0000000000..283e378443
--- /dev/null
+++ b/engines/toon/state.h
@@ -0,0 +1,101 @@
+/* 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 TOON_STATE_H
+#define TOON_STATE_H
+
+#include "common/file.h"
+#include "common/str.h"
+#include "toon/conversation.h"
+
+namespace Toon {
+
+struct Location {
+ char _name[64];
+ char _music[64];
+ char _cutaway[64];
+ bool _visited;
+ int32 _numSceneAnimations;
+ int32 _flags;
+ int32 _numRifBoxes;
+ int16 _rifBoxesFlags[256];
+
+ void save(Common::WriteStream *stream);
+ void load(Common::ReadStream *stream);
+};
+
+class State {
+public:
+ State(void);
+ ~State(void);
+
+ Location _locations[256];
+ int16 _gameGlobalData[256];
+ uint8 _gameFlag[256];
+ int16 _lastVisitedScene;
+ int16 _currentScene;
+ int16 _currentScrollValue;
+ bool _currentScrollLock;
+ int16 _inventory[35];
+ int16 _confiscatedInventory[35];
+ int32 _numInventoryItems;
+ int32 _numConfiscatedInventoryItems;
+ bool _inCloseUp;
+ bool _inCutaway;
+ bool _inConversation;
+ bool _inInventory;
+ bool _showConversationIcons;
+ int16 _mouseState;
+ int16 *_conversationData;
+ Conversation _conversationState[60];
+ int16 _currentConversationId;
+ bool _firstConverstationLine;
+ bool _exitConversation;
+ bool _mouseHidden;
+ bool _sackVisible;
+ int32 _gameTimer;
+ int8 _currentChapter;
+ int32 _nextSpecialEnterX;
+ int32 _nextSpecialEnterY;
+
+
+ bool _timerEnabled[2];
+ int32 _timerTimeout[2];
+ int32 _timerDelay[2];
+
+ int32 getGameFlag(int32 flagId);
+ bool hasItemInInventory(int32 item);
+
+ void load(Common::ReadStream *stream);
+ void save(Common::WriteStream *stream);
+
+ void loadConversations(Common::ReadStream *stream);
+ void saveConversations(Common::WriteStream *stream);
+
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/text.cpp b/engines/toon/text.cpp
new file mode 100644
index 0000000000..c18e0cbdc8
--- /dev/null
+++ b/engines/toon/text.cpp
@@ -0,0 +1,95 @@
+/* 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 "toon/text.h"
+
+namespace Toon {
+
+
+TextResource::TextResource(ToonEngine *vm) : _vm(vm) {
+ _numTexts = 0;
+ _textData = 0;
+}
+
+TextResource::~TextResource(void) {
+
+}
+
+bool TextResource::loadTextResource(Common::String fileName) {
+ debugC(1, kDebugText, "loadTextResource(%s)", fileName.c_str());
+
+ uint32 fileSize = 0;
+ uint8 *data = _vm->resources()->getFileData(fileName, &fileSize);
+ if (!data)
+ return false;
+
+ _textData = new uint8[fileSize];
+ memcpy(_textData, data, fileSize);
+ _numTexts = READ_LE_UINT16(data);
+
+ return true;
+}
+
+int32 TextResource::getNext(int32 offset) {
+ debugC(1, kDebugText, "getNext(%d)", offset);
+
+ uint16 *table = (uint16 *)_textData + 1;
+ int a = getId(offset);
+ return table[a+1];
+}
+
+int32 TextResource::getId(int32 offset) {
+ debugC(1, kDebugText, "getId(%d)", offset);
+
+ uint16 *table = (uint16 *)_textData + 1;
+ int32 found = -1;
+ for (int32 i = 0; i < _numTexts; i++) {
+ if (offset == table[i]) {
+ found = i;
+ break;
+ }
+ }
+ return found;
+}
+
+char *TextResource::getText(int32 offset) {
+ debugC(6, kDebugText, "getText(%d)", offset);
+
+ uint16 *table = (uint16 *)_textData + 1;
+ int32 found = -1;
+ for (int32 i = 0; i < _numTexts; i++) {
+ if (offset == table[i]) {
+ found = i;
+ break;
+ }
+ }
+ if (found < 0)
+ return NULL;
+
+ int32 realOffset = ((uint16 *)_textData + 1 + _numTexts)[found];
+ return (char *)_textData + realOffset;
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/text.h b/engines/toon/text.h
new file mode 100644
index 0000000000..9a35471e4f
--- /dev/null
+++ b/engines/toon/text.h
@@ -0,0 +1,51 @@
+/* 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 TOON_TEXT_H
+#define TOON_TEXT_H
+
+#include "toon/toon.h"
+
+namespace Toon {
+
+class TextResource {
+public:
+ TextResource(ToonEngine *vm);
+ ~TextResource(void);
+
+ bool loadTextResource(Common::String fileName);
+ char *getText(int32 id);
+ int32 getId(int32 offset);
+ int32 getNext(int32 offset);
+
+protected:
+ int32 _numTexts;
+ uint8 *_textData;
+ ToonEngine *_vm;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/tools.cpp b/engines/toon/tools.cpp
new file mode 100644
index 0000000000..a03a2d57ce
--- /dev/null
+++ b/engines/toon/tools.cpp
@@ -0,0 +1,516 @@
+/* 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 "toon/tools.h"
+#include "toon/toon.h"
+
+namespace Toon {
+
+uint32 decompressLZSS(byte *src, byte *dst, int dstsize) {
+ debugC(5, kDebugTools, "decompressLZSS(src, dst, %d)", dstsize);
+
+ byte *srcp = src;
+ byte *dstp = dst;
+ uint16 bitbuf;
+ int32 len, ofs;
+ len = 0;
+ while (dstsize > 0) {
+ bitbuf = 0x100 | *(srcp++);
+ while (bitbuf != 1 && dstsize > 0) {
+ if (bitbuf & 1) {
+ ofs = READ_LE_UINT16(srcp);
+ srcp += 2;
+ len = ((ofs & 0xF000) >> 12) + 3;
+ ofs = ofs | 0xF000;
+ dstsize -= len;
+ if (dstsize < 0)
+ break;
+ while (len--) {
+ *dstp = *(byte *)(dstp + (signed short)ofs);
+ dstp++;
+ }
+ } else {
+ len = 0;
+ while ((bitbuf & 2) == 0) {
+ len++;
+ bitbuf >>= 1;
+ }
+ len++;
+ dstsize -= len;
+ if (dstsize < 0)
+ break;
+ while (len--)
+ *(dstp++) = *(srcp++);
+ }
+ bitbuf >>= 1;
+ }
+ }
+ len += dstsize;
+ if (len < 0)
+ return 0;
+
+ while (len--)
+ *(dstp++) = *(srcp++);
+
+ return (dstp - dst);
+}
+
+uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize) {
+ debugC(1, kDebugTools, "decompressSPCN(src, dst, %d)", dstsize);
+
+ byte *srcp = src;
+ byte *dstp = dst, *dste = dst + dstsize;
+ byte val;
+ uint16 len, ofs;
+ if (!(*srcp & 0x80)) srcp++;
+ while (dstp < dste) {
+ val = *(srcp++);
+ if (val & 0x80) {
+ if (val & 0x40) {
+ if (val == 0xFE) {
+ len = READ_LE_UINT16(srcp);
+ while (len--)
+ *(dstp++) = srcp[2];
+ srcp += 3;
+ } else {
+ if (val == 0xFF) {
+ len = READ_LE_UINT16(srcp);
+ srcp += 2;
+ } else {
+ len = (val & 0x3F) + 3;
+ }
+ ofs = READ_LE_UINT16(srcp);
+ srcp += 2;
+ while (len--) {
+ *dstp = *(byte *)(dstp - ofs);
+ dstp++;
+ }
+ }
+ } else {
+ len = val & 0x3F;
+ while (len--)
+ *(dstp++) = *(srcp++);
+ }
+ } else {
+ len = (val >> 4) + 3;
+ ofs = ((val & 0x0F) << 8) | *(srcp++);
+ while (len--) {
+ *dstp = *(byte *)(dstp - ofs);
+ dstp++;
+ }
+ }
+ }
+ return (dstp - dst);
+}
+
+
+//return codes
+#define NOT_PACKED 0
+#define PACKED_CRC -1
+#define UNPACKED_CRC -2
+
+//other defines
+#define TABLE_SIZE (16 * 8)
+#define MIN_LENGTH 2
+#define HEADER_LEN 18
+
+RncDecoder::RncDecoder() {
+ initCrc();
+}
+
+RncDecoder::~RncDecoder() { }
+
+void RncDecoder::initCrc() {
+ debugC(1, kDebugTools, "initCrc()");
+
+ uint16 cnt = 0;
+ uint16 tmp1 = 0;
+ uint16 tmp2 = 0;
+
+ for (tmp2 = 0; tmp2 < 0x100; tmp2++) {
+ tmp1 = tmp2;
+ for (cnt = 8; cnt > 0; cnt--) {
+ if (tmp1 % 2) {
+ tmp1 >>= 1;
+ tmp1 ^= 0x0a001;
+ } else
+ tmp1 >>= 1;
+ }
+ _crcTable[tmp2] = tmp1;
+ }
+}
+
+//calculate 16 bit crc of a block of memory
+uint16 RncDecoder::crcBlock(const uint8 *block, uint32 size) {
+ debugC(1, kDebugTools, "crcBlock(block, %d)", size);
+
+ uint16 crc = 0;
+ uint8 *crcTable8 = (uint8 *)_crcTable; //make a uint8* to crc_table
+ uint8 tmp;
+ uint32 i;
+
+ for (i = 0; i < size; i++) {
+ tmp = *block++;
+ crc ^= tmp;
+ tmp = (uint8)((crc >> 8) & 0x00FF);
+ crc &= 0x00FF;
+ crc = *(uint16 *)&crcTable8[crc << 1];
+ crc ^= tmp;
+ }
+
+ return crc;
+}
+
+uint16 RncDecoder::inputBits(uint8 amount) {
+ debugC(5, kDebugTools, "inputBits(%d)", amount);
+
+ uint16 newBitBuffh = _bitBuffh;
+ uint16 newBitBuffl = _bitBuffl;
+ int16 newBitCount = _bitCount;
+ uint16 remBits, returnVal;
+
+ returnVal = ((1 << amount) - 1) & newBitBuffl;
+ newBitCount -= amount;
+
+ if (newBitCount < 0) {
+ newBitCount += amount;
+ remBits = (newBitBuffh << (16 - newBitCount));
+ newBitBuffh >>= newBitCount;
+ newBitBuffl >>= newBitCount;
+ newBitBuffl |= remBits;
+ _srcPtr += 2;
+ newBitBuffh = READ_LE_UINT16(_srcPtr);
+ amount -= newBitCount;
+ newBitCount = 16 - amount;
+ }
+ remBits = (newBitBuffh << (16 - amount));
+ _bitBuffh = newBitBuffh >> amount;
+ _bitBuffl = (newBitBuffl >> amount) | remBits;
+ _bitCount = (uint8)newBitCount;
+
+ return returnVal;
+}
+
+void RncDecoder::makeHufftable(uint16 *table) {
+ debugC(1, kDebugTools, "makeHufftable(table)");
+
+ uint16 bitLength, i, j;
+ uint16 numCodes = inputBits(5);
+
+ if (!numCodes)
+ return;
+
+ uint8 huffLength[16];
+ for (i = 0; i < numCodes; i++)
+ huffLength[i] = (uint8)(inputBits(4) & 0x00FF);
+
+ uint16 huffCode = 0;
+
+ for (bitLength = 1; bitLength < 17; bitLength++) {
+ for (i = 0; i < numCodes; i++) {
+ if (huffLength[i] == bitLength) {
+ *table++ = (1 << bitLength) - 1;
+
+ uint16 b = huffCode >> (16 - bitLength);
+ uint16 a = 0;
+
+ for (j = 0; j < bitLength; j++)
+ a |= ((b >> j) & 1) << (bitLength - j - 1);
+ *table++ = a;
+
+ *(table + 0x1e) = (huffLength[i] << 8) | (i & 0x00FF);
+ huffCode += 1 << (16 - bitLength);
+ }
+ }
+ }
+}
+
+uint16 RncDecoder::inputValue(uint16 *table) {
+ debugC(5, kDebugTools, "inputValue(table)");
+
+ uint16 valOne, valTwo, value = _bitBuffl;
+
+ do {
+ valTwo = (*table++) & value;
+ valOne = *table++;
+ } while (valOne != valTwo);
+
+ value = *(table + 0x1e);
+ inputBits((uint8)((value >> 8) & 0x00FF));
+ value &= 0x00FF;
+
+ if (value >= 2) {
+ value--;
+ valOne = inputBits((uint8)value & 0x00FF);
+ valOne |= (1 << value);
+ value = valOne;
+ }
+
+ return value;
+}
+
+int RncDecoder::getbit() {
+ debugC(6, kDebugTools, "getbits()");
+
+ if (_bitCount == 0) {
+ _bitBuffl = *_srcPtr++;
+ _bitCount = 8;
+ }
+ byte temp = (_bitBuffl & 0x80) >> 7;
+ _bitBuffl <<= 1;
+ _bitCount--;
+ return temp;
+}
+
+int32 RncDecoder::unpackM1(const void *input, void *output) {
+ debugC(1, kDebugTools, "unpackM1(input, output)");
+
+ uint8 *outputLow, *outputHigh;
+ const uint8 *inputHigh, *inputptr = (const uint8 *)input;
+
+ uint32 unpackLen = 0;
+ uint32 packLen = 0;
+ uint16 counts = 0;
+ uint16 crcUnpacked = 0;
+ uint16 crcPacked = 0;
+
+
+ _bitBuffl = 0;
+ _bitBuffh = 0;
+ _bitCount = 0;
+
+ //Check for "RNC "
+ if (READ_BE_UINT32(inputptr) != RNC1_SIGNATURE)
+ return NOT_PACKED;
+
+ inputptr += 4;
+
+ // read unpacked/packed file length
+ unpackLen = READ_BE_UINT32(inputptr);
+ inputptr += 4;
+ packLen = READ_BE_UINT32(inputptr);
+ inputptr += 4;
+
+ uint8 blocks = *(inputptr + 5);
+
+ //read CRC's
+ crcUnpacked = READ_BE_UINT16(inputptr);
+ inputptr += 2;
+ crcPacked = READ_BE_UINT16(inputptr);
+ inputptr += 2;
+ inputptr = (inputptr + HEADER_LEN - 16);
+
+ if (crcBlock(inputptr, packLen) != crcPacked)
+ return PACKED_CRC;
+
+ inputptr = (((const uint8 *)input) + HEADER_LEN);
+ _srcPtr = inputptr;
+
+ inputHigh = ((const uint8 *)input) + packLen + HEADER_LEN;
+ outputLow = (uint8 *)output;
+ outputHigh = *(((const uint8 *)input) + 16) + unpackLen + outputLow;
+
+ if (!((inputHigh <= outputLow) || (outputHigh <= inputHigh))) {
+ _srcPtr = inputHigh;
+ _dstPtr = outputHigh;
+ memcpy((_dstPtr - packLen), (_srcPtr - packLen), packLen);
+ _srcPtr = (_dstPtr - packLen);
+ }
+
+ _dstPtr = (uint8 *)output;
+ _bitCount = 0;
+
+ _bitBuffl = READ_LE_UINT16(_srcPtr);
+ inputBits(2);
+
+ do {
+ makeHufftable(_rawTable);
+ makeHufftable(_posTable);
+ makeHufftable(_lenTable);
+
+ counts = inputBits(16);
+
+ do {
+ uint32 inputLength = inputValue(_rawTable);
+ uint32 inputOffset;
+
+ if (inputLength) {
+ memcpy(_dstPtr, _srcPtr, inputLength); //memcpy is allowed here
+ _dstPtr += inputLength;
+ _srcPtr += inputLength;
+ uint16 a = READ_LE_UINT16(_srcPtr);
+ uint16 b = READ_LE_UINT16(_srcPtr + 2);
+
+ _bitBuffl &= ((1 << _bitCount) - 1);
+ _bitBuffl |= (a << _bitCount);
+ _bitBuffh = (a >> (16 - _bitCount)) | (b << _bitCount);
+ }
+
+ if (counts > 1) {
+ inputOffset = inputValue(_posTable) + 1;
+ inputLength = inputValue(_lenTable) + MIN_LENGTH;
+
+ // Don't use memcpy here! because input and output overlap.
+ uint8 *tmpPtr = (_dstPtr - inputOffset);
+ while (inputLength--)
+ *_dstPtr++ = *tmpPtr++;
+ }
+ } while (--counts);
+ } while (--blocks);
+
+ if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
+ return UNPACKED_CRC;
+
+ // all is done..return the amount of unpacked bytes
+ return unpackLen;
+}
+
+int32 RncDecoder::unpackM2(const void *input, void *output) {
+ debugC(1, kDebugTools, "unpackM2(input, output)");
+
+ const uint8 *inputptr = (const uint8 *)input;
+
+ uint32 unpackLen = 0;
+ uint32 packLen = 0;
+ uint16 crcUnpacked = 0;
+ uint16 crcPacked = 0;
+
+// Strangerke - Commented (not used)
+// uint16 counts = 0;
+
+ _bitBuffl = 0;
+ _bitCount = 0;
+
+ //Check for "RNC "
+ if (READ_BE_UINT32(inputptr) != RNC2_SIGNATURE)
+ return NOT_PACKED;
+
+ inputptr += 4;
+
+ // read unpacked/packed file length
+ unpackLen = READ_BE_UINT32(inputptr);
+ inputptr += 4;
+ packLen = READ_BE_UINT32(inputptr);
+ inputptr += 4;
+
+ //read CRC's
+ crcUnpacked = READ_BE_UINT16(inputptr);
+ inputptr += 2;
+ crcPacked = READ_BE_UINT16(inputptr);
+ inputptr += 2;
+ inputptr = (inputptr + HEADER_LEN - 16);
+
+ if (crcBlock(inputptr, packLen) != crcPacked)
+ return PACKED_CRC;
+
+ inputptr = (((const uint8 *)input) + HEADER_LEN);
+ _srcPtr = inputptr;
+ _dstPtr = (uint8 *)output;
+
+
+ uint16 ofs, len;
+ byte ofs_hi, ofs_lo;
+
+ len = 0;
+ ofs_hi = 0;
+ ofs_lo = 0;
+
+ getbit();
+ getbit();
+
+ while (1) {
+
+ bool loadVal = false;
+
+ while (getbit() == 0)
+ *_dstPtr++ = *_srcPtr++;
+
+ len = 2;
+ ofs_hi = 0;
+ if (getbit() == 0) {
+ len = (len << 1) | getbit();
+ if (getbit() == 1) {
+ len--;
+ len = (len << 1) | getbit();
+ if (len == 9) {
+ len = 4;
+ while (len--)
+ ofs_hi = (ofs_hi << 1) | getbit();
+ len = (ofs_hi + 3) * 4;
+ while (len--)
+ *_dstPtr++ = *_srcPtr++;
+ continue;
+ }
+ }
+ loadVal = true;
+ } else {
+ if (getbit() == 1) {
+ len++;
+ if (getbit() == 1) {
+ len = *_srcPtr++;
+ if (len == 0) {
+ if (getbit() == 1)
+ continue;
+ else
+ break;
+ }
+ len += 8;
+ }
+ loadVal = true;
+ } else {
+ loadVal = false;
+ }
+ }
+
+ if (loadVal) {
+ if (getbit() == 1) {
+ ofs_hi = (ofs_hi << 1) | getbit();
+ if (getbit() == 1) {
+ ofs_hi = ((ofs_hi << 1) | getbit()) | 4;
+ if (getbit() == 0)
+ ofs_hi = (ofs_hi << 1) | getbit();
+ } else if (ofs_hi == 0) {
+ ofs_hi = 2 | getbit();
+ }
+ }
+ }
+
+ ofs_lo = *_srcPtr++;
+ ofs = (ofs_hi << 8) | ofs_lo;
+ while (len--) {
+ *_dstPtr = *(byte *)(_dstPtr - ofs - 1);
+ _dstPtr++;
+ }
+
+ }
+
+ if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
+ return UNPACKED_CRC;
+
+ // all is done..return the amount of unpacked bytes
+ return unpackLen;
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/tools.h b/engines/toon/tools.h
new file mode 100644
index 0000000000..05fc5c9cda
--- /dev/null
+++ b/engines/toon/tools.h
@@ -0,0 +1,83 @@
+/* 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 TOON_TOOLS_H
+#define TOON_TOOLS_H
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+
+#define RNC1_SIGNATURE 0x524E4301 // "RNC\001"
+#define RNC2_SIGNATURE 0x524E4302 // "RNC\002"
+
+namespace Toon {
+
+const uint32 kCompLZSS = 0x4C5A5353;
+const uint32 kCompSPCN = 0x5350434E;
+const uint32 kCompRNC1 = 0x524E4301;
+const uint32 kCompRNC2 = 0x524E4302;
+
+#define READ_LE_INT16(x) (int16) READ_LE_UINT16(x)
+#define READ_LE_INT32(x) (int32) READ_LE_UINT32(x)
+
+#define WRITE_LE_INT16(x,y) WRITE_LE_UINT16(x,(int16)y)
+#define WRITE_LE_INT32(x,y) WRITE_LE_UINT32(x,(int32)y)
+
+uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize);
+uint32 decompressLZSS(byte *src, byte *dst, int dstsize);
+
+class RncDecoder {
+
+protected:
+ uint16 _rawTable[64];
+ uint16 _posTable[64];
+ uint16 _lenTable[64];
+ uint16 _crcTable[256];
+
+ uint16 _bitBuffl;
+ uint16 _bitBuffh;
+ uint8 _bitCount;
+
+ const uint8 *_srcPtr;
+ uint8 *_dstPtr;
+
+public:
+ RncDecoder();
+ ~RncDecoder();
+ int32 unpackM1(const void *input, void *output);
+ int32 unpackM2(const void *input, void *output);
+
+protected:
+ void initCrc();
+ uint16 crcBlock(const uint8 *block, uint32 size);
+ uint16 inputBits(uint8 amount);
+ void makeHufftable(uint16 *table);
+ uint16 inputValue(uint16 *table);
+ int getbit();
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp
new file mode 100644
index 0000000000..7489f5c5d9
--- /dev/null
+++ b/engines/toon/toon.cpp
@@ -0,0 +1,4401 @@
+/* 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/events.h"
+#include "common/debug-channels.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/EventRecorder.h"
+#include "engines/util.h"
+#include "graphics/surface.h"
+#include "graphics/thumbnail.h"
+#include "gui/saveload.h"
+#include "gui/about.h"
+#include "gui/message.h"
+#include "toon/resource.h"
+#include "toon/toon.h"
+#include "toon/anim.h"
+#include "toon/picture.h"
+#include "toon/hotspot.h"
+#include "toon/flux.h"
+#include "toon/drew.h"
+#include "toon/path.h"
+
+namespace Toon {
+
+
+void ToonEngine::init() {
+ _currentScriptRegion = 0;
+ _resources = new Resources(this);
+ _animationManager = new AnimationManager(this);
+ _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer));
+ _hotspots = new Hotspots(this);
+
+ _mainSurface = new Graphics::Surface();
+ _mainSurface->create(1280, 400, 1);
+
+ _finalPalette = new uint8[768];
+ _backupPalette = new uint8[768];
+ _additionalPalette1 = new uint8[69];
+ _additionalPalette2 = new uint8[69];
+ _cutawayPalette = new uint8[768];
+ _universalPalette = new uint8[96];
+ _fluxPalette = new uint8[24];
+
+ memset(_finalPalette, 0, 768);
+ memset(_backupPalette, 0, 768);
+ memset(_additionalPalette1, 0, 69);
+ memset(_additionalPalette2, 0, 69);
+ memset(_cutawayPalette, 0, 768);
+ memset(_universalPalette, 0, 96);
+ memset(_fluxPalette, 0, 24);
+
+ _conversationData = new int16[4096];
+ memset(_conversationData, 0, 4096 * sizeof(int16));
+
+ _shouldQuit = false;
+ _scriptStep = 0;
+
+ _cursorOffsetX = 0;
+ _cursorOffsetY = 0;
+ _currentHotspotItem = 0;
+
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ _currentTextLineX = 0;
+ _currentTextLineY = 0;
+ _currentTextLineCharacterId = 0;
+
+ _saveBufferStream = new Common::MemoryWriteStreamDynamic();
+
+ _firstFrame = false;
+
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "misc");
+
+ syncSoundSettings();
+
+ _pathFinding = new PathFinding(this);
+
+ resources()->openPackage("misc/local.pak", true);
+ resources()->openPackage("misc/onetime.pak", true);
+ resources()->openPackage("misc/drew.pak", true);
+
+ for (int32 i = 0; i < 32; i++)
+ _characters[i] = 0;
+
+ _characters[0] = new CharacterDrew(this);
+ _characters[1] = new CharacterFlux(this);
+ _drew = _characters[0];
+ _flux = _characters[1];
+
+ // preload walk anim for flux and drew
+ _drew->loadWalkAnimation("stndwalk.caf");
+ _drew->setupPalette();
+ _drew->loadShadowAnimation("shadow.caf");
+
+ _flux->loadWalkAnimation("fxstwalk.caf");
+ _flux->loadShadowAnimation("shadow.caf");
+
+ loadAdditionalPalette("universe.pal", 3);
+ loadAdditionalPalette("flux.pal", 4);
+ setupGeneralPalette();
+
+ _script_func = new ScriptFunc(this);
+ _gameState = new State();
+ _gameState->_conversationData = _conversationData;
+
+ memset(_sceneAnimations, 0, sizeof(_sceneAnimations));
+ memset(_sceneAnimationScripts, 0, sizeof(_sceneAnimationScripts));
+
+
+ _gameState->_currentChapter = 1;
+ initChapter();
+ loadCursor();
+ initFonts();
+
+ _dialogIcons = new Animation(this);
+ _dialogIcons->loadAnimation("dialogue.caf");
+
+ _inventoryIcons = new Animation(this);
+ _inventoryIcons->loadAnimation("inventry.caf");
+
+ _inventoryIconSlots = new Animation(this);
+ _inventoryIconSlots->loadAnimation("iconslot.caf");
+
+ _genericTexts = new TextResource(this);
+ _genericTexts->loadTextResource("generic.tre");
+
+ _audioManager = new AudioManager(this, _mixer);
+ _audioManager->loadAudioPack(0, "generic.svi", "misc/generic.svl");
+ _audioManager->loadAudioPack(2, "generic.sei", "generic.sel");
+
+ _lastMouseButton = 0;
+ _mouseButton = 0;
+}
+
+void ToonEngine::waitForScriptStep() {
+ // Wait after a specified number of script steps when executing a script
+ // to lower CPU usage
+ if (++_scriptStep >= 40) {
+ g_system->delayMillis(10);
+ _scriptStep = 0;
+ }
+}
+
+void ToonEngine::parseInput() {
+
+ Common::EventManager *_event = _system->getEventManager();
+
+ _mouseX = _event->getMousePos().x;
+ _mouseY = _event->getMousePos().y;
+ _mouseButton = _event->getButtonState();
+
+ Common::Event event;
+ while (_event->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYUP:
+ if (event.kbd.ascii == 27 || event.kbd.ascii == 32) {
+ _audioManager->stopCurrentVoice();
+ }
+ if (event.kbd.keycode == Common::KEYCODE_F5) {
+ saveGame(-1);
+ }
+ if (event.kbd.keycode == Common::KEYCODE_F6) {
+ loadGame(-1);
+ }
+
+ if (event.kbd.flags & Common::KBD_ALT) {
+ int32 slotNum = event.kbd.ascii - '0';
+ if (slotNum >= 0 && slotNum <= 9) {
+ if (saveGame(slotNum)) {
+ // ok
+ char buf[256];
+ snprintf(buf, 256, "Saved game in slot #%d ", slotNum);
+ GUI::TimedMessageDialog dialog(buf, 1000);
+ dialog.runModal();
+ } else {
+ char buf[256];
+ snprintf(buf, 256, "Could not quick save into slot #%d", slotNum);
+ GUI::MessageDialog dialog2(buf, "OK", 0);
+ //warning("%s", buf);
+ dialog2.runModal();
+
+ }
+ }
+ }
+
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ int32 slotNum = event.kbd.ascii - '0';
+ if (slotNum >= 0 && slotNum <= 9) {
+ if (loadGame(slotNum)) {
+ // ok
+ char buf[256];
+ snprintf(buf, 256, "Savegame #%d quick loaded", slotNum);
+ GUI::TimedMessageDialog dialog(buf, 1000);
+ dialog.runModal();
+ } else {
+ char buf[256];
+ snprintf(buf, 256, "Could not quick load the savegame #%d", slotNum);
+ GUI::MessageDialog dialog(buf, "OK", 0);
+ warning("%s", buf);
+ dialog.runModal();
+ }
+ }
+ }
+ break;
+// Strangerke - Commented (not used)
+// case Common::EVENT_LBUTTONDOWN:
+// break;
+// case Common::EVENT_RBUTTONDOWN:
+// break;
+// case Common::EVENT_LBUTTONUP:
+// break;
+// case Common::EVENT_RBUTTONUP:
+// break;
+// case Common::EVENT_WHEELUP:
+// break;
+// case Common::EVENT_WHEELDOWN:
+// break;
+ default:
+ break;
+ }
+ }
+
+ if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory) {
+ selectHotspot();
+ clickEvent();
+ }
+
+}
+
+void ToonEngine::enableTimer(int32 timerId) {
+ _gameState->_timerEnabled[timerId] = true;
+}
+void ToonEngine::setTimer(int32 timerId, int32 timerWait) {
+ _gameState->_timerTimeout[timerId] = getOldMilli() + timerWait * getTickLength();
+ _gameState->_timerDelay[timerId] = timerWait;
+}
+void ToonEngine::disableTimer(int32 timerId) {
+ _gameState->_timerEnabled[timerId] = false;
+}
+void ToonEngine::updateTimers() {
+ for (int32 i = 0; i < 2; i++) {
+ if (_gameState->_timerEnabled[i]) {
+ if (_gameState->_timerDelay[i] > -1 && getOldMilli() > _gameState->_timerTimeout[i]) {
+ if (i == 0) {
+
+ EMCState *status = &_scriptState[_currentScriptRegion];
+ _script->init(status, &_scriptData);
+
+ // setup registers
+ status->regs[0] = _mouseX;
+ status->regs[1] = _mouseY;
+ status->regs[2] = 0;
+
+ _currentScriptRegion++;
+
+ _script->start(status, 7);
+ while (_script->run(status))
+ waitForScriptStep();
+
+ _currentScriptRegion--;
+
+ _gameState->_timerTimeout[i] = getOldMilli() + _gameState->_timerDelay[i] * getTickLength();
+
+ return;
+
+ }
+ }
+ }
+ }
+}
+
+void ToonEngine::updateScrolling(bool force, int32 timeIncrement) {
+ static int32 lastScrollOffset = 320;
+ if (!_audioManager->voiceStillPlaying() && !_gameState->_currentScrollLock && (_drew->getFlag() & 1) == 0) {
+ if (_drew->getFacing() & 3) {
+ if (_drew->getFacing() <= 4)
+ lastScrollOffset = 200;
+ else
+ lastScrollOffset = 440;
+ }
+
+ if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
+ return;
+
+ int32 desiredScrollValue = _drew->getX() - lastScrollOffset;
+
+ if ((_gameState->_locations[_gameState->_currentScene]._flags & 0x80) == 0) {
+ if (desiredScrollValue < 0)
+ desiredScrollValue = 0;
+ if (desiredScrollValue >= _currentPicture->getWidth() - 640)
+ desiredScrollValue = _currentPicture->getWidth() - 640;
+
+ if (force) {
+ _gameState->_currentScrollValue = desiredScrollValue;
+ return;
+ } else {
+ if (_gameState->_currentScrollValue < desiredScrollValue) {
+ _gameState->_currentScrollValue += timeIncrement / 2;
+
+ if (_gameState->_currentScrollValue > desiredScrollValue)
+ _gameState->_currentScrollValue = desiredScrollValue;
+ } else if (_gameState->_currentScrollValue > desiredScrollValue) {
+ _gameState->_currentScrollValue -= timeIncrement / 2;
+
+ if (_gameState->_currentScrollValue < desiredScrollValue)
+ _gameState->_currentScrollValue = desiredScrollValue;
+ }
+ }
+ }
+ }
+}
+
+void ToonEngine::update(int32 timeIncrement) {
+ // to make sure we're updating the game at 5fps at least
+ if (timeIncrement > 200)
+ timeIncrement = 200;
+
+ updateAnimationSceneScripts(timeIncrement);
+ updateCharacters(timeIncrement);
+ updateTimer(timeIncrement);
+ updateTimers();
+ updateScrolling(false, timeIncrement);
+ _animationManager->update(timeIncrement);
+ _cursorAnimationInstance->update(timeIncrement);
+
+ if (!_audioManager->voiceStillPlaying()) {
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+}
+
+void ToonEngine::updateTimer(int32 timeIncrement) {
+ if (_gameState->_gameTimer > 0) {
+ debugC(0, 0xfff, "updateTimer(%d)", timeIncrement);
+ _gameState->_gameTimer -= timeIncrement;
+ if (_gameState->_gameTimer < 0)
+ _gameState->_gameTimer = 0;
+ }
+}
+
+void ToonEngine::render() {
+ if (_gameState->_inCutaway)
+ _currentCutaway->draw(*_mainSurface, 0, 0, 0, 0);
+ else
+ _currentPicture->draw(*_mainSurface, 0, 0, 0, 0);
+
+ //_currentMask->drawMask(*_mainSurface,0,0,0,0);
+ _animationManager->render();
+
+ drawInfoLine();
+ drawConversationLine();
+ drawConversationIcons();
+ drawSack();
+ //drawPalette();
+
+#if 0
+ char test[256];
+ if (_mouseX > 0 && _mouseX < 640 && _mouseY > 0 && _mouseY < 400) {
+ sprintf(test, "%d %d / mask %d layer %d z %d", _mouseX, _mouseY, getMask()->getData(_mouseX, _mouseY), getLayerAtPoint(_mouseX, _mouseY), getZAtPoint(_mouseX, _mouseY));
+
+ int32 c = *(uint8 *)_mainSurface->getBasePtr(_mouseX, _mouseY);
+ sprintf(test, "%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c*3+0], _finalPalette[c*3+1], _finalPalette[c*3+2]);
+
+ _fontRenderer->setFont(_fontToon);
+ _fontRenderer->renderText(40, 150, Common::String(test), 0);
+ }
+#endif
+
+ if (_firstFrame) {
+ copyToVirtualScreen(false);
+ fadeIn(5);
+ _firstFrame = false;
+ } else {
+ copyToVirtualScreen(true);
+ }
+}
+
+void ToonEngine::copyToVirtualScreen(bool updateScreen) {
+ // render cursor last
+ if (!_gameState->_mouseHidden) {
+ _cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false);
+ _cursorAnimationInstance->render();
+ }
+ _system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, 1280, 0, 0, 640, 400);
+ if (updateScreen) {
+ _system->updateScreen();
+ _shouldQuit = shouldQuit(); // update game quit flag - this shouldn't be called all the time, as it's a virtual function
+ }
+}
+
+void ToonEngine::doFrame() {
+
+ if (_gameState->_inInventory) {
+ renderInventory();
+ } else {
+ render();
+
+ int32 currentTimer = _system->getMillis();
+// Strangerke - Commented (not used)
+// int32 elapsedTime = currentTimer - _oldTimer;
+
+ update(currentTimer - _oldTimer);
+ _oldTimer = currentTimer;
+ _oldTimer2 = currentTimer;
+ }
+ parseInput();
+}
+
+enum MainMenuSelections {
+ MAINMENUHOTSPOT_NONE = 0,
+ MAINMENUHOTSPOT_START = 1,
+ MAINMENUHOTSPOT_INTRO = 2,
+ MAINMENUHOTSPOT_LOADGAME = 3,
+ MAINMENUHOTSPOT_HOTKEYS = 4,
+ MAINMENUHOTSPOT_CREDITS = 5,
+ MAINMENUHOTSPOT_QUIT = 6,
+ MAINMENUHOTSPOT_HOTKEYSCLOSE = 7
+};
+
+enum MainMenuMasks {
+ MAINMENUMASK_BASE = 1,
+ MAINMENUMASK_HOTKEYS = 2,
+ MAINMENUMASK_EVERYWHERE = 3
+};
+
+struct MainMenuFile {
+ int menuMask;
+ int id;
+ const char *animationFile;
+ int animateOnFrame;
+};
+
+#define MAINMENU_ENTRYCOUNT 12
+static const MainMenuFile mainMenuFiles[] = {
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_START, "STARTBUT.CAF", 0 }, // "Start" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_INTRO, "INTROBUT.CAF", 0 }, // "Intro" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_LOADGAME, "LOADBUT.CAF", 0 }, // "Load Game" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_HOTKEYS, "HOTBUT.CAF", 0 }, // "Hot Keys" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_CREDITS, "CREDBUT.CAF", 0 }, // "Credits" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_QUIT, "QUITBUT.CAF", 0 }, // "Quit" button
+ { MAINMENUMASK_BASE, MAINMENUHOTSPOT_NONE, "LEGALTXT.CAF", 0 }, // Legal Text
+
+ { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONGLOW.CAF", 6 }, // Clown glow
+ { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONSTRK.CAF", 6 }, // Toonstruck title
+ { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "EYEGLOW.CAF", 4 }, // Clown eye glow
+ { MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "PROPHEAD.CAF", 4 }, // Clown propellor head
+ { MAINMENUMASK_HOTKEYS, MAINMENUHOTSPOT_HOTKEYSCLOSE, "HOTKEYS.CAF", 0 } // Hotkeys display - clicking on it will close hotkeys
+};
+
+struct MainMenuEntry {
+ int menuMask;
+ int id;
+ Animation *animation;
+ Common::Rect rect;
+ int animateOnFrame;
+ int animateCurFrame;
+ int activeFrame;
+};
+
+bool ToonEngine::showMainmenu(bool &loadedGame) {
+ Picture *mainmenuPicture = new Picture(this);
+ mainmenuPicture->loadPicture("TITLESCR.CPS", true);
+ mainmenuPicture->setupPalette();
+
+ MainMenuEntry entries[MAINMENU_ENTRYCOUNT];
+
+ for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
+ entries[entryNr].menuMask = mainMenuFiles[entryNr].menuMask;
+ entries[entryNr].id = mainMenuFiles[entryNr].id;
+ entries[entryNr].animation = new Animation(this);
+ entries[entryNr].animation->loadAnimation(mainMenuFiles[entryNr].animationFile);
+ if (entries[entryNr].id != MAINMENUHOTSPOT_NONE)
+ entries[entryNr].rect = entries[entryNr].animation->getRect();
+ entries[entryNr].animateOnFrame = mainMenuFiles[entryNr].animateOnFrame;
+ entries[entryNr].animateCurFrame = 0;
+ entries[entryNr].activeFrame = 0;
+ }
+
+ setCursor(1);
+
+ bool doExit = false;
+ bool exitGame = false;
+ int clickingOn, clickRelease;
+ int menuMask = MAINMENUMASK_BASE;
+ AudioStreamInstance *mainmenuMusic = NULL;
+ bool musicPlaying = false;
+
+ while (!doExit) {
+ clickingOn = MAINMENUHOTSPOT_NONE;
+ clickRelease = false;
+
+ if (!musicPlaying) {
+ Common::SeekableReadStream *mainmenuMusicFile = resources()->openFile("misc/BR091013.MUS");
+ mainmenuMusic = new AudioStreamInstance(_audioManager, _mixer, mainmenuMusicFile, true);
+ mainmenuMusic->play(false);
+ musicPlaying = true;
+ }
+
+ while (!clickRelease) {
+ mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0);
+
+ for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
+ if (entries[entryNr].menuMask & menuMask) {
+ if (entries[entryNr].animateOnFrame) {
+ entries[entryNr].animateCurFrame++;
+ if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) {
+ entries[entryNr].activeFrame++;
+ if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames)
+ entries[entryNr].activeFrame = 0;
+ entries[entryNr].animateCurFrame = 0;
+ }
+ }
+ int32 frameNr = entries[entryNr].activeFrame;
+ if ((entries[entryNr].id == clickingOn) && (clickingOn != MAINMENUHOTSPOT_NONE))
+ frameNr = 1;
+ entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0);
+ }
+ }
+
+ parseInput();
+ copyToVirtualScreen(true);
+ _system->delayMillis(17);
+
+ if (_mouseButton & 1) {
+ // left mouse button pushed down
+ clickingOn = MAINMENUHOTSPOT_NONE;
+ for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
+ if (entries[entryNr].menuMask & menuMask) {
+ if (entries[entryNr].id != MAINMENUHOTSPOT_NONE) {
+ if (entries[entryNr].rect.contains(_mouseX, _mouseY))
+ clickingOn = entries[entryNr].id;
+ }
+ }
+ }
+ } else {
+ // left mouse button released/not pushed down
+ if (clickingOn != MAINMENUHOTSPOT_NONE)
+ clickRelease = true;
+ }
+ if (_shouldQuit) {
+ clickingOn = MAINMENUHOTSPOT_NONE;
+ clickRelease = true;
+ doExit = true;
+ }
+ }
+
+ if (clickingOn != MAINMENUHOTSPOT_NONE) {
+ _audioManager->playSFX(10, 128, true);
+ }
+
+ switch (clickingOn) {
+ case MAINMENUHOTSPOT_HOTKEYS:
+ menuMask = MAINMENUMASK_HOTKEYS;
+ continue;
+ case MAINMENUHOTSPOT_HOTKEYSCLOSE:
+ menuMask = MAINMENUMASK_BASE;
+ continue;
+ }
+
+ if (musicPlaying) {
+ //stop music
+ mainmenuMusic->stop(false);
+ musicPlaying = false;
+ }
+
+
+
+ switch (clickingOn) {
+ case MAINMENUHOTSPOT_START:
+ // Start game (actually exit main menu)
+ loadedGame = false;
+ doExit = true;
+ break;
+ case MAINMENUHOTSPOT_INTRO:
+ // Play intro movies
+ getMoviePlayer()->play("MISC/209_1M.SMK", 0x10);
+ getMoviePlayer()->play("MISC/209_2M.SMK", 0x10);
+ getMoviePlayer()->play("MISC/209_3M.SMK", 0x10);
+ break;
+ case MAINMENUHOTSPOT_LOADGAME:
+ doExit = loadGame(-1);
+ loadedGame = doExit;
+ exitGame = false;
+ break;
+ case MAINMENUHOTSPOT_CREDITS:
+ // Play credits movie
+ getMoviePlayer()->play("MISC/CREDITS.SMK", 0x0);
+ break;
+ case MAINMENUHOTSPOT_QUIT:
+ exitGame = true;
+ doExit = true;
+ break;
+ }
+ }
+
+ return !exitGame;
+}
+
+Common::Error ToonEngine::run() {
+
+ if (!loadToonDat())
+ return Common::kUnknownError;
+
+ g_eventRec.registerRandomSource(_rnd, "toon");
+
+ initGraphics(640, 400, true);
+ init();
+
+ // play producer intro
+ getMoviePlayer()->play("MISC/VIELOGOM.SMK", 0x10);
+
+ // show mainmenu
+ bool loadedGame = false;
+ if (!showMainmenu(loadedGame)) {
+ return Common::kNoError;
+ }
+
+ //loadScene(17);
+ //loadScene(37);
+ if (!loadedGame) {
+ newGame();
+ }
+
+// Strangerke - Commented (not used)
+// int32 oldTimer = _system->getMillis();
+ while (!_shouldQuit && _gameState->_currentScene != -1)
+ doFrame();
+ return Common::kNoError;
+}
+
+ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
+ : Engine(syst), _gameDescription(gameDescription), _language(gameDescription->language) {
+ _system = syst;
+ _tickLength = 16;
+ _currentMask = 0;
+ _currentPicture = 0;
+ _roomScaleData = 0;
+ _shadowLUT = 0;
+ _isDemo = _gameDescription->flags & ADGF_DEMO;
+
+ DebugMan.addDebugChannel(kDebugAnim, "Anim", "Animation debug level");
+ DebugMan.addDebugChannel(kDebugCharacter, "Character", "Character debug level");
+ DebugMan.addDebugChannel(kDebugAudio, "Audio", "Audio debug level");
+ DebugMan.addDebugChannel(kDebugHotspot, "Hotspot", "Hotspot debug level");
+ DebugMan.addDebugChannel(kDebugFont, "Font", "Font debug level");
+ DebugMan.addDebugChannel(kDebugPath, "Path", "Path debug level");
+ DebugMan.addDebugChannel(kDebugMovie, "Movie", "Movie debug level");
+ DebugMan.addDebugChannel(kDebugPicture, "Picture", "Picture debug level");
+ DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level");
+ DebugMan.addDebugChannel(kDebugState, "State", "State debug level");
+ DebugMan.addDebugChannel(kDebugTools, "Tools", "Tools debug level");
+ DebugMan.addDebugChannel(kDebugText, "Text", "Text debug level");
+
+ switch (_language) {
+ case Common::EN_GRB:
+ case Common::EN_USA:
+ case Common::EN_ANY:
+ _gameVariant = 0;
+ break;
+ case Common::FR_FRA:
+ _gameVariant = 1;
+ break;
+ case Common::DE_DEU:
+ _gameVariant = 2;
+ break;
+ case Common::RU_RUS:
+ _gameVariant = 3;
+ break;
+ case Common::ES_ESP:
+ _gameVariant = 3;
+ break;
+ default:
+ // 0 - english
+ _gameVariant = 0;
+ break;
+ }
+}
+
+ToonEngine::~ToonEngine() {
+
+}
+
+void ToonEngine::flushPalette() {
+
+ uint8 vmpalette[1024];
+ for (int32 i = 0; i < 256; i++) {
+ vmpalette[i*4+0] = _finalPalette[i*3+0];
+ vmpalette[i*4+1] = _finalPalette[i*3+1];
+ vmpalette[i*4+2] = _finalPalette[i*3+2];
+ vmpalette[i*4+3] = 0;
+ }
+ _system->setPalette(vmpalette, 0, 256);
+}
+void ToonEngine::setPaletteEntries(uint8 *palette, int32 offset, int32 num) {
+ memcpy(_finalPalette + offset * 3, palette, num * 3);
+ uint8 vmpalette[1024];
+ for (int32 i = 0; i < num; i++) {
+ vmpalette[i*4+0] = palette[i*3+0];
+ vmpalette[i*4+1] = palette[i*3+1];
+ vmpalette[i*4+2] = palette[i*3+2];
+ vmpalette[i*4+3] = 0;
+ }
+ _system->setPalette(vmpalette, offset, num);
+}
+
+void ToonEngine::simpleUpdate() {
+ int32 elapsedTime = _system->getMillis() - _oldTimer2;
+ _oldTimer2 = _system->getMillis();
+ _oldTimer = _oldTimer2;
+
+ updateCharacters(elapsedTime);
+ updateAnimationSceneScripts(elapsedTime);
+ updateTimer(elapsedTime);
+ _animationManager->update(elapsedTime);
+ render();
+
+ if (!_audioManager->voiceStillPlaying()) {
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+}
+
+void ToonEngine::fixPaletteEntries(uint8 *palette, int num) {
+ // some color values are coded on 6bits ( for old 6bits DAC )
+ for (int32 i = 0; i < num * 3; i++) {
+ int32 a = palette[i];
+ a = a * 4;
+ if (a > 255)
+ a = 255;
+ palette[i] = a;
+ }
+}
+
+// adapted from KyraEngine
+void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) {
+
+
+ static int32 numReentrant = 0;
+ numReentrant++;
+
+// Strangerke - Commented (not used)
+// uint32 nextTime = _system->getMillis() + _tickLength;
+ const int startScript = _lastProcessedSceneScript;
+
+ _updatingSceneScriptRunFlag = true;
+
+ do {
+ if (_sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() &&
+ !_sceneAnimationScripts[_lastProcessedSceneScript]._frozen) {
+ _animationSceneScriptRunFlag = true;
+
+ while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) {
+ if (!_script->run(&_sceneAnimationScripts[_lastProcessedSceneScript]._state))
+ _animationSceneScriptRunFlag = false;
+
+ //waitForScriptStep();
+
+ if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen)
+ break;
+ }
+
+ }
+
+ if (!_script->isValid(&_sceneAnimationScripts[_lastProcessedSceneScript]._state)) {
+ _script->start(&_sceneAnimationScripts[_lastProcessedSceneScript]._state, 9 + _lastProcessedSceneScript);
+ _animationSceneScriptRunFlag = false;
+ }
+
+ ++_lastProcessedSceneScript;
+ if (_lastProcessedSceneScript >= state()->_locations[state()->_currentScene]._numSceneAnimations)
+ _lastProcessedSceneScript = 0;
+
+ } while (_lastProcessedSceneScript != startScript && !_shouldQuit);
+
+
+ _updatingSceneScriptRunFlag = false;
+ numReentrant--;
+
+}
+
+void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
+ char temp[256];
+ char temp2[256];
+
+
+ _firstFrame = true;
+
+ _gameState->_lastVisitedScene = _gameState->_currentScene;
+ _gameState->_currentScene = SceneId;
+
+ _saveBufferStream->seek(0);
+
+ if (SceneId == -1) {
+ // this scene -1 is loaded at the very end of the game
+ getAudioManager()->stopMusic();
+ getMoviePlayer()->play("CREDITS.SMK");
+ return;
+ }
+
+ // find out in what chapter we are (the script function ProcessToNextChapter is actually not called )
+ // the location flag has the chapter info in it
+ int32 flag = _gameState->_locations[SceneId]._flags;
+ if (flag) {
+ _gameState->_currentChapter = 0;
+ do {
+ _gameState->_currentChapter++;
+ flag >>= 1;
+ } while ((flag & 1) == 0);
+ }
+
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i]) _characters[i]->setFlag(0);
+ }
+ _drew->playStandingAnim();
+ _drew->setVisible(true);
+
+ // hide flux in chapter 2
+ if (_gameState->_currentChapter == 1) {
+ _flux->playStandingAnim();
+ _flux->setVisible(true);
+ } else {
+ _flux->setVisible(false);
+ }
+
+ _lastMouseButton = 0;
+ _mouseButton = 0;
+ _currentHotspotItem = -1;
+ _gameState->_sackVisible = true;
+ _gameState->_inCloseUp = false;
+ _gameState->_inConversation = false;
+ _gameState->_inInventory = false;
+ _gameState->_inCutaway = false;
+ _gameState->_currentScrollValue = 0;
+ _gameState->_currentScrollLock = false;
+ _gameState->_inCloseUp = false;
+
+
+ if (_gameState->_mouseState >= 0)
+ addItemToInventory(_gameState->_mouseState);
+
+ _gameState->_mouseState = -1;
+
+
+ // load package
+ strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ resources()->openPackage(temp, true);
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".npp");
+ loadAdditionalPalette(temp, 0);
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".np2");
+ loadAdditionalPalette(temp, 1);
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".cup");
+ loadAdditionalPalette(temp, 2);
+
+ // load artwork
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".cps");
+ _currentPicture = new Picture(this);
+ _currentPicture->loadPicture(temp);
+ _currentPicture->setupPalette();
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".msc");
+ _currentMask = new Picture(this);
+ if (_currentMask->loadPicture(temp))
+ _pathFinding->init(_currentMask);
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".tre");
+ _roomTexts = new TextResource(this);
+ _roomTexts->loadTextResource(temp);
+
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".dat");
+ uint32 fileSize;
+ uint8 *sceneData = resources()->getFileData(temp, &fileSize);
+ if (sceneData) {
+ _roomScaleData = new uint8[fileSize];
+ memcpy(_roomScaleData, sceneData, fileSize);
+ }
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".svi");
+ strcpy(temp2, createRoomFilename(Common::String::printf("%s.svl", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ _audioManager->loadAudioPack(1, temp, temp2);
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcpy(temp2, state()->_locations[SceneId]._name);
+ strcat(temp, ".sei");
+ strcat(temp2, ".sel");
+ _audioManager->loadAudioPack(3, temp, temp2);
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".ric");
+ if (state()->_locations[SceneId]._flags & 0x40) {
+ strcpy(temp2, state()->_locations[SceneId]._cutaway);
+ strcat(temp2, ".ric");
+ } else {
+ strcpy(temp2, "");
+ }
+ _hotspots->LoadRif(temp, temp2);
+ restoreRifFlags(_gameState->_currentScene);
+
+
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".cnv");
+ uint32 convfileSize;
+ uint8 *convData = resources()->getFileData(temp, &convfileSize);
+ if (convData) {
+ assert(convfileSize < 4096 * sizeof(int16));
+ memcpy(_conversationData , convData, convfileSize);
+ prepareConversations();
+ }
+
+ // load script
+ strcpy(temp, state()->_locations[SceneId]._name);
+ strcat(temp, ".emc");
+
+ _oldTimer = _system->getMillis();
+ _oldTimer2 = _oldTimer;
+
+ // fix the weird scaling issue during one frame when entering new scene
+ _drew->update(0);
+ _flux->update(0);
+
+
+ _script->load(temp, &_scriptData, &_script_func->_opcodes);
+ _script->init(&_scriptState[0], &_scriptData);
+ _script->init(&_scriptState[1], &_scriptData);
+ _script->init(&_scriptState[2], &_scriptData);
+ _script->init(&_scriptState[3], &_scriptData);
+
+ //_script->RoomScript->Decompile("decomp.txt");
+ //RoomScript->Decompile2("decomp2.txt");
+
+ for (int i = 0; i < state()->_locations[SceneId]._numSceneAnimations; i++) {
+ _sceneAnimationScripts[i]._data = &_scriptData;
+ _script->init(&_sceneAnimationScripts[i]._state, _sceneAnimationScripts[i]._data);
+ if (!forGameLoad) {
+ _script->start(&_sceneAnimationScripts[i]._state, 9 + i);
+ _sceneAnimationScripts[i]._lastTimer = getSystem()->getMillis();
+ _sceneAnimationScripts[i]._frozen = false;
+ }
+ }
+
+ // launch mus
+#if 0
+ SoundManager.StopMusic();
+ SoundManager.UnloadMusic();
+ SoundManager.LoadMusic(GameLocations[Scene].name, GameLocations[Scene].mus);
+ SoundManager.PlayMusic();
+#endif
+
+ playRoomMusic();
+
+ _lastProcessedSceneScript = 0;
+ _gameState->_locations[SceneId]._visited = true;
+
+
+ setupGeneralPalette();
+ createShadowLUT();
+
+
+
+ if (!forGameLoad) {
+
+ _script->start(&_scriptState[0], 0);
+
+ while (_script->run(&_scriptState[0]))
+ waitForScriptStep();
+
+ _script->start(&_scriptState[0], 8);
+
+ while (_script->run(&_scriptState[0]))
+ waitForScriptStep();
+
+ if (_gameState->_nextSpecialEnterX != -1 && _gameState->_nextSpecialEnterY != -1) {
+ _drew->setPosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY);
+ _gameState->_nextSpecialEnterX = -1;
+ _gameState->_nextSpecialEnterY = -1;
+ }
+
+ _script->start(&_scriptState[0], 3);
+
+ while (_script->run(&_scriptState[0]))
+ waitForScriptStep();
+
+ _script->start(&_scriptState[0], 4);
+
+ while (_script->run(&_scriptState[0]))
+ waitForScriptStep();
+
+ }
+
+ state()->_mouseHidden = false;
+}
+
+void ToonEngine::setupGeneralPalette() {
+ setPaletteEntries(_additionalPalette1, 232, 23);
+ setPaletteEntries(_universalPalette, 200, 32);
+ setPaletteEntries(_fluxPalette, 192, 8);
+
+ if (_drew)
+ _drew->setupPalette();
+}
+
+void ToonEngine::loadAdditionalPalette(Common::String fileName, int32 mode) {
+
+ uint32 size = 0;
+ uint8 *palette = resources()->getFileData(fileName, &size);
+ if (!palette)
+ return;
+
+ switch (mode) {
+ case 0:
+ memcpy(_additionalPalette1, palette, 69);
+ fixPaletteEntries(_additionalPalette1, 23);
+ break;
+ case 1:
+ memcpy(_additionalPalette2, palette, 69);
+ fixPaletteEntries(_additionalPalette2, 23);
+ break;
+ case 2:
+ memcpy(_cutawayPalette, palette, 768);
+ fixPaletteEntries(_cutawayPalette, 256);
+ break;
+ case 3:
+ memcpy(_universalPalette, palette, 96);
+ fixPaletteEntries(_universalPalette, 32);
+ break;
+ case 4:
+ memcpy(_fluxPalette, palette, 24);
+ fixPaletteEntries(_fluxPalette , 8);
+ break;
+ default:
+ warning("loadAdditionalPalette() - Unknown mode");
+ }
+}
+
+void ToonEngine::initChapter() {
+
+ EMCData data;
+ EMCState status;
+ memset(&data, 0, sizeof(data));
+ memset(&status, 0, sizeof(status));
+
+ _script = new EMCInterpreter(this);
+
+ _script->load("_START01.EMC", &data, &_script_func->_opcodes);
+ _script->init(&status, &data);
+ _script->start(&status, 0);
+ while (_script->run(&status))
+ waitForScriptStep();
+
+ setupGeneralPalette();
+
+}
+
+void ToonEngine::loadCursor() {
+ _cursorAnimation = new Animation(this);
+ _cursorAnimation->loadAnimation("MOUSE.CAF");
+ _cursorAnimationInstance = _animationManager->createNewInstance(kAnimationCursor);
+ _cursorAnimationInstance->setAnimation(_cursorAnimation);
+ _cursorAnimationInstance->setVisible(true);
+ _cursorAnimationInstance->setFrame(0);
+ _cursorAnimationInstance->setAnimationRange(0, 0);
+ _cursorAnimationInstance->setFps(8);
+
+ setCursor(5);
+}
+
+void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int32 offsetY) {
+
+ static const int32 offsets[] = {
+ 0, 1, 1, 6, 7, 1, 8, 10, 18, 10,
+ 28, 8, 36, 10, 46, 10, 56, 10, 66, 10,
+ 76, 10, 86, 10, 96, 10, 106, 10, 116, 10,
+ 126, 10
+ };
+
+ if (!inventory) {
+ _cursorAnimationInstance->setAnimation(_cursorAnimation);
+ _cursorAnimationInstance->setAnimationRange(offsets[type * 2 + 0], offsets[type * 2 + 0] + offsets[type * 2 + 1] - 1);
+ _cursorAnimationInstance->playAnimation();
+ } else {
+ _cursorAnimationInstance->setAnimation(_inventoryIcons);
+ _cursorAnimationInstance->setAnimationRange(type, type);
+ _cursorAnimationInstance->playAnimation();
+ }
+
+ _cursorOffsetX = offsetX;
+ _cursorOffsetY = offsetY;
+}
+
+void ToonEngine::setSceneAnimationScriptUpdate(bool enable) {
+ _animationSceneScriptRunFlag = enable;
+}
+
+bool ToonEngine::isUpdatingSceneAnimation() {
+ return _updatingSceneScriptRunFlag;
+}
+
+int32 ToonEngine::getCurrentUpdatingSceneAnimation() {
+ return _lastProcessedSceneScript;
+}
+
+int32 ToonEngine::randRange(int32 minStart, int32 maxStart) {
+ return _rnd.getRandomNumberRng(minStart, maxStart);
+}
+
+int32 ToonEngine::runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId) {
+
+ if (_currentScriptRegion >= 4)
+ return 0;
+
+ EMCState *status = &_scriptState[_currentScriptRegion];
+ _script->init(status, &_scriptData);
+
+ // setup registers
+ status->regs[0] = x;
+ status->regs[1] = y;
+ status->regs[2] = 0;
+ status->regs[3] = 0;
+ status->regs[4] = _gameState->_mouseState; //
+ status->regs[5] = 0;
+ status->regs[6] = scriptId;
+ status->regs[7] = mode;
+ status->regs[8] = id;
+
+ _currentScriptRegion++;
+
+ _script->start(status, 1);
+ while (_script->run(status))
+ waitForScriptStep();
+
+ _currentScriptRegion--;
+
+ return status->regs[2];
+}
+
+void ToonEngine::clickEvent() {
+ bool leftButton = false;
+ bool rightButton = false;
+
+ if ((_lastMouseButton & 0x1) == 1 && (_mouseButton & 0x1) == 0)
+ leftButton = true;
+ if ((_lastMouseButton & 0x2) == 2 && (_mouseButton & 0x2) == 0)
+ rightButton = true;
+
+ _lastMouseButton = _mouseButton;
+ if (!leftButton && !rightButton)
+ return;
+
+ if (_gameState->_sackVisible) {
+ if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
+ if (_gameState->_mouseState >= 0 && !rightButton) {
+ addItemToInventory(_gameState->_mouseState);
+ setCursor(0, false, 0, 0);
+ _currentHotspotItem = -1;
+ return;
+ } else {
+ showInventory();
+ }
+ return;
+ }
+ }
+
+ // with inventory
+ if (rightButton && _gameState->_mouseState >= 0) {
+ addItemToInventory(_gameState->_mouseState);
+ setCursor(0, false, 0, 0);
+ _currentHotspotItem = -1;
+ return;
+ }
+
+
+ int32 mouseX = _mouseX;
+ if (_gameState->_inCutaway) {
+ mouseX += 1280;
+ }
+
+ // find hotspot
+ int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue , _mouseY);
+ HotspotData *currentHot = 0;
+ if (hot > -1) {
+ currentHot = _hotspots->Get(hot);
+ }
+
+ if (_currentHotspotItem == -3) {
+ if (_gameState->_mouseState <= 0) {
+ if (leftButton)
+ createMouseItem(104);
+ else
+ characterTalk(518);
+ }
+ }
+ if (_currentHotspotItem == -4) {
+ if (_gameState->_mouseState >= 0) {
+ if (leftButton)
+ if (!handleInventoryOnInventory(0, _gameState->_mouseState)) {
+ playSoundWrong();
+ }
+ return;
+ }
+ }
+
+
+ if (!currentHot) {
+ int32 xx, yy;
+
+ if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
+ return;
+
+ if (_pathFinding->findClosestWalkingPoint(_mouseX + _gameState->_currentScrollValue , _mouseY, &xx, &yy))
+ _drew->walkTo(xx, yy);
+ return;
+ }
+
+ int commandId = 0;
+ if (_gameState->_mouseState < 0) {
+ // left or right click
+ if (rightButton)
+ commandId = 2 + 8;
+ else
+ commandId = 0 + 8;
+ } else {
+ commandId = 2 * (_gameState->_mouseState - 1) + 16;
+ }
+
+ _drew->stopWalk();
+
+ int16 command = currentHot->getData(commandId);
+ int16 argument = currentHot->getData(commandId + 1);
+ int16 priority = currentHot->getPriority();
+// Strangerke - Commented (not used)
+// int16 ref = currentHot->getRef();
+// int16 pad1 = currentHot->getData(6);
+
+ if (!_gameState->_inCutaway && !_gameState->_inCloseUp) {
+ if (leftButton && (currentHot->getData(4) != 2 || _gameState->_mouseState >= 0) && currentHot->getData(5) != -1) {
+ if (currentHot->getData(5)) {
+ if (!_drew->walkTo(currentHot->getData(5), currentHot->getData(6))) {
+ // walk was canceled ?
+ return;
+ }
+ } else {
+ if (!_drew->walkTo(_mouseX, _mouseY)) {
+ // walk was canceled ?
+ return;
+ }
+ }
+ }
+ }
+
+ int32 result = 0;
+
+ switch (command) {
+ case 1:
+ sayLines(1, argument);
+ break;
+ case 2:
+ result = runEventScript(_mouseX, _mouseY, command, argument, priority);
+ break;
+ case 3:
+ runEventScript(_mouseX, _mouseY, command, argument, priority);
+ result = 0;
+ break;
+ case 4:
+ playSFX(argument, 128);
+ break;
+ case 5:
+ break;
+ case 6:
+ createMouseItem(argument);
+ currentHot->setData(7, -1);
+ break;
+ case 7:
+ // switch to CloseUp
+// Strangerke - Commented (not used)
+// int closeup = 1;
+ break;
+ case 8:
+ // face flux
+ sayLines(1, argument);
+ break;
+ case 9:
+ case 10:
+// Strangerke - Commented (not used)
+// if (rand() % 1 == 1) {
+// } else {
+// }
+ // setFluxFacingPoint(x,y)
+ sayLines(2, argument);
+ break;
+ case 11:
+ sayLines(3, argument);
+ break;
+ default:
+ playSoundWrong();
+ return;
+ }
+
+ if (result == 3) {
+ int32 val = _scriptState[_currentScriptRegion].regs[4];
+ currentHot->setData(4, currentHot->getData(4) & val);
+ }
+ if (result == 2 || result == 3) {
+ int32 val = _scriptState[_currentScriptRegion].regs[6];
+ currentHot->setData(7, val);
+ }
+
+ if (result == 1) {
+ int32 val = _scriptState[_currentScriptRegion].regs[4];
+ currentHot->setData(4, currentHot->getData(4) & val);
+ }
+
+}
+
+void ToonEngine::selectHotspot() {
+ int32 x1 = 0;
+ int32 x2 = 0;
+ int32 y1 = 0;
+ int32 y2 = 0;
+
+ int32 mouseX = _mouseX;
+
+ if (_gameState->_inCutaway)
+ mouseX += 1280;
+
+ if (_gameState->_sackVisible) {
+ if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
+ _currentHotspotItem = -2;
+
+ if (_gameState->_mouseState < 0) {
+ int mode = 3;
+ setCursor(mode);
+ } else {
+ setCursor(_gameState->_mouseState, true, -18, -14);
+ }
+
+ return;
+ }
+ }
+
+ if (_gameState->_mouseState > 0) {
+ // picked drew?
+ getDrew()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
+ if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
+ _currentHotspotItem = -4;
+ return;
+ }
+ }
+
+ if (getFlux()->getVisible()) {
+ getFlux()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
+ if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
+ _currentHotspotItem = -3;
+
+ if (_gameState->_mouseState < 0) {
+ int mode = 3;
+ setCursor(mode);
+ } else {
+ setCursor(_gameState->_mouseState, true, -18, -14);
+ }
+
+ return;
+ }
+ }
+
+ int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue, _mouseY);
+ if (hot != -1) {
+ HotspotData *hotspot = _hotspots->Get(hot);
+ int32 item = hotspot->getData(14);
+ if (hotspot->getType() == 3)
+ item += 2000;
+
+ // update palette based on ticks if we're in "use from inventory mode"
+ if (_gameState->_mouseState >= 0) {
+
+ int32 tick = _system->getMillis() / _tickLength;
+ int32 animReverse = tick & 0x10;
+ int32 animStep = tick & 0xf;
+
+ byte color[3];
+ if (animReverse == 0) {
+ color[0] = 16 * animStep;
+ color[1] = 0;
+ color[2] = 0;
+ } else {
+ color[0] = 16 * (15 - animStep);
+ color[1] = 0;
+ color[2] = 0;
+ }
+ setPaletteEntries(color, 255, 1);
+ }
+
+#if 0
+ if (item == _currentHotspotItem)
+ return;
+#endif
+ _currentHotspotItem = item;
+ if (_gameState->_mouseState < 0) {
+ int mode = hotspot->getMode();
+ setCursor(mode);
+ } else {
+ setCursor(_gameState->_mouseState, true, -18, -14);
+ }
+ } else {
+ _currentHotspotItem = 0;
+
+ if (_gameState->_mouseState < 0) {
+ setCursor(0);
+ } else {
+ byte color[3];
+ color[0] = 0;
+ color[1] = 0;
+ color[2] = 0;
+ setCursor(_gameState->_mouseState, true, -18, -14);
+ setPaletteEntries(color, 255, 1);
+ }
+ }
+}
+
+void ToonEngine::exitScene() {
+
+ fadeOut(5);
+
+ // disable all scene animation
+ for (int32 i = 0; i < 64; i++) {
+ if (_sceneAnimations[i]._active) {
+ delete _sceneAnimations[i]._animation;
+ _sceneAnimations[i]._active = false;
+ _animationManager->removeInstance(_sceneAnimations[i]._animInstance);
+ _sceneAnimations[i]._animInstance = 0;
+ _sceneAnimations[i]._animation = 0;
+ }
+ }
+ for (int32 i = 0; i < 64; i++) {
+ _sceneAnimationScripts[i]._frozen = true;
+ _sceneAnimationScripts[i]._active = false;
+ }
+
+ // remove all characters except drew and flux
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i] != _drew && _characters[i] != _flux) {
+ if (_characters[i]) {
+ delete _characters[i];
+ _characters[i] = 0;
+ }
+ } else {
+ _characters[i]->stopSpecialAnim();
+ }
+ }
+
+ for (int32 i = 0; i < 2; i++) {
+ _gameState->_timerEnabled[i] = false;
+ }
+
+ // put back our item if inventory if needed
+ if (_gameState->_mouseState >= 0) {
+ addItemToInventory(_gameState->_mouseState);
+ _gameState->_mouseState = -1;
+ }
+
+ char temp[256];
+ strcpy(temp, createRoomFilename(Common::String::printf("%s.pak", _gameState->_locations[_gameState->_currentScene]._name).c_str()).c_str());
+ resources()->closePackage(temp);
+
+ _drew->stopWalk();
+ _flux->stopWalk();
+
+ storeRifFlags(_gameState->_currentScene);
+}
+
+// flip between the cutaway scene and the normal scene
+void ToonEngine::flipScreens() {
+ _gameState->_inCloseUp = !_gameState->_inCloseUp;
+
+ if (_gameState->_inCloseUp) {
+ _gameState->_currentScrollValue = 640;
+ setPaletteEntries(_cutawayPalette, 1, 128);
+ setPaletteEntries(_additionalPalette2, 232, 23);
+ } else {
+ _gameState->_currentScrollValue = 0;
+ _currentPicture->setupPalette();
+ setupGeneralPalette();
+ }
+ flushPalette();
+}
+
+void ToonEngine::fadeIn(int32 numFrames) {
+ for (int32 f = 0; f < numFrames; f++) {
+
+ uint8 vmpalette[1024];
+ for (int32 i = 0; i < 256; i++) {
+ vmpalette[i*4+0] = f * _finalPalette[i*3+0] / (numFrames - 1);
+ vmpalette[i*4+1] = f * _finalPalette[i*3+1] / (numFrames - 1);
+ vmpalette[i*4+2] = f * _finalPalette[i*3+2] / (numFrames - 1);
+ vmpalette[i*4+3] = 0;
+ }
+ _system->setPalette(vmpalette, 0, 256);
+ _system->updateScreen();
+ _system->delayMillis(_tickLength);
+ }
+}
+
+void ToonEngine::fadeOut(int32 numFrames) {
+ for (int32 f = 0; f < numFrames; f++) {
+
+ uint8 vmpalette[1024];
+ for (int32 i = 0; i < 256; i++) {
+ vmpalette[i*4+0] = (numFrames - f - 1) * _finalPalette[i*3+0] / (numFrames - 1);
+ vmpalette[i*4+1] = (numFrames - f - 1) * _finalPalette[i*3+1] / (numFrames - 1);
+ vmpalette[i*4+2] = (numFrames - f - 1) * _finalPalette[i*3+2] / (numFrames - 1);
+ vmpalette[i*4+3] = (numFrames - f - 1);
+ }
+ _system->setPalette(vmpalette, 0, 256);
+ _system->updateScreen();
+ _system->delayMillis(_tickLength);
+ }
+}
+
+void ToonEngine::initFonts() {
+ _fontRenderer = new FontRenderer(this);
+ _fontToon = new Animation(this);
+ _fontToon->loadAnimation("misc/toonfont.caf");
+
+ _fontEZ = new Animation(this);
+ _fontEZ->loadAnimation("misc/ezfont.caf");
+}
+
+void ToonEngine::drawInfoLine() {
+ if (_currentHotspotItem != 0 && !_gameState->_mouseHidden && !_gameState->_inConversation) {
+ const char *infoTool = NULL;
+ if (_currentHotspotItem >= 0 && _currentHotspotItem < 2000) {
+ infoTool = _roomTexts->getText(_currentHotspotItem);
+ } else if (_currentHotspotItem <= -1) {
+// static const char * const specialInfoLine[] = { "Exit non defined", "Bottomless Bag", "Flux", "Drew Blanc" };
+ infoTool = _specialInfoLine[-1 - _currentHotspotItem];
+ } else {
+ int32 loc = _currentHotspotItem - 2000;
+ // location names are hardcoded ...
+ infoTool = getLocationString(loc, _gameState->_locations[loc]._visited);
+ }
+ if (infoTool) {
+ _fontRenderer->setFontColor(0xc8, 0xdd, 0xe3);
+ _fontRenderer->setFont(_fontToon);
+ _fontRenderer->renderText(320 + _gameState->_currentScrollValue, 398, infoTool, 5);
+ }
+ }
+}
+
+const char *ToonEngine::getLocationString(int32 locationId, bool alreadyVisited) {
+ if (alreadyVisited)
+ return _locationDirVisited[locationId];
+ else
+ return _locationDirNotVisited[locationId];
+}
+
+int32 ToonEngine::getScaleAtPoint(int32 x, int32 y) {
+ if (!_currentMask)
+ return 1024;
+
+ int32 maskData = _currentMask->getData(x, y) & 0x1f;
+ return _roomScaleData[maskData+2] * 1024 / 100;
+}
+
+int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) {
+ if (!_currentMask)
+ return 0;
+
+ int32 maskData = _currentMask->getData(x, y) & 0x1f;
+ return _roomScaleData[maskData+130] << 5;
+}
+
+int32 ToonEngine::getZAtPoint(int32 x, int32 y) {
+ if (!_currentMask)
+ return 0;
+ return _currentMask->getData(x, y) & 0x1f;
+}
+
+void ToonEngine::storeRifFlags(int32 location) {
+
+ if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount()) {
+ _gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
+ }
+
+ for (int32 i = 0; i < _hotspots->getCount(); i++) {
+ _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4);
+ _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7);
+ }
+}
+
+void ToonEngine::restoreRifFlags(int32 location) {
+ if (_hotspots) {
+ if (!_gameState->_locations[location]._visited) {
+ for (int32 i = 0; i < _hotspots->getCount(); i++) {
+ _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4);
+ _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7);
+ }
+ _gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
+ } else {
+ if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount())
+ return;
+
+ for (int32 i = 0; i < _hotspots->getCount(); i++) {
+ _hotspots->Get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]);
+ _hotspots->Get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]);
+ }
+ }
+ }
+}
+
+void ToonEngine::sayLines(int numLines, int dialogId) {
+ // exit conversation state
+
+ // if (inInventory)
+ // character_talks(dialogid, -1, 0, 0);
+ // else
+
+#if 0
+ int oldShowMouse = 0;
+
+ if (Game.MouseHiddenCount <= 0) {
+ Game.MouseHiddenCount = 1;
+ oldShowMouse = 1;
+ }
+#endif
+
+ int32 currentLine = dialogId;
+
+ for (int32 i = 0; i < numLines; i++) {
+ if (!characterTalk(currentLine))
+ break;
+
+ while (_audioManager->voiceStillPlaying() && !_shouldQuit)
+ doFrame();
+
+ // find next line
+ if (currentLine < 1000)
+ currentLine = _roomTexts->getNext(currentLine);
+ else
+ currentLine = _genericTexts->getNext(currentLine - 1000) + 1000;
+ }
+
+#if 0
+ if (oldShowMouse)
+ Game.MouseHiddenCount = 0;
+#endif
+
+}
+
+int32 ToonEngine::simpleCharacterTalk(int32 dialogid) {
+ int32 myId = 0;
+
+// Strangerke - Commented (not used)
+#if 0
+ char *myLine;
+ if (dialogid < 1000) {
+ myLine = _roomTexts->getText(dialogid);
+ myId = dialogid;
+ } else {
+ myLine = _genericTexts->getText(dialogid - 1000);
+ myId = dialogid - 1000;
+ }
+ debugC(0, 0xfff, "Talker = %d will say '%s' \n", READ_LE_UINT16(c - 2), myLine);
+#endif
+
+ if (_audioManager->voiceStillPlaying())
+ _audioManager->stopCurrentVoice();
+
+ if (dialogid < 1000) {
+ myId = _roomTexts->getId(dialogid);
+ _audioManager->playVoice(myId, false);
+ } else {
+ myId = _genericTexts->getId(dialogid - 1000);
+ _audioManager->playVoice(myId, true);
+ }
+
+ return 1;
+}
+
+void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker) {
+ if (animID || talker) {
+// Strangerke - Commented (not used)
+#if 0
+ if (_gameState->_inCutaway || _gameState->_inInventory) {
+ if (talker) {
+ // character talks
+ }
+ } else
+#endif
+ if (characterId == 0) {
+ _drew->playAnim(animID, 0, (talker ? 8 : 0) + 2);
+ } else if (characterId == 1) {
+ // stop flux if he is walking
+ if (_flux->getFlag() & 1) {
+ _flux->stopWalk();
+ }
+ _flux->playAnim(animID, 0, (talker ? 8 : 0) + 2);
+ _flux->setFlag(_flux->getFlag() | 1);
+ } else {
+ Character *character = getCharacterById(characterId);
+ if (character) {
+ character->playAnim(animID, 0, (talker ? 8 : 0) + 2);
+ }
+ }
+ } else {
+ Character *character = getCharacterById(characterId);
+ if (character)
+ character->setAnimFlag(character->getAnimFlag() | 1);
+ }
+}
+
+int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
+ if (blocking == false && _audioManager->voiceStillPlaying()) {
+ // someone is already talking, and this voice is not important
+ // skip it
+ return 0;
+ }
+
+ int32 myId = 0;
+ char *myLine;
+ if (dialogid < 1000) {
+ myLine = _roomTexts->getText(dialogid);
+ myId = dialogid;
+ } else {
+ myLine = _genericTexts->getText(dialogid - 1000);
+ myId = dialogid - 1000;
+ }
+
+ if (!myLine)
+ return 0;
+
+ bool oldMouseHidden = _gameState->_mouseHidden;
+ if (blocking) {
+ _gameState->_mouseHidden = true;
+ }
+
+
+ // get what is before the string
+ int a = READ_LE_UINT16(myLine - 2);
+ char *b = myLine - 2 - 4 * a;
+
+ char *c = b - 2; // v6
+ int numParticipants = READ_LE_UINT16(c); // num dialogue participants
+
+ char *e = c - 2 - 4 * numParticipants;
+ READ_LE_UINT16(e);
+
+// Strangerke - Commented (not used)
+// char *g = e - 2 * f;
+
+ // flag as talking
+// Strangerke - Commented (not used)
+// char *h = c;
+
+
+ // if one voice is still playing, wait !
+ if (blocking) {
+ while (_audioManager->voiceStillPlaying() && !_shouldQuit)
+ doFrame();
+
+ char *cc = c;
+ Character *waitChar;
+ for (int32 i = 0; i < numParticipants - 1; i++) {
+ // listener
+ int32 listenerId = READ_LE_UINT16(cc - 2);
+ cc -= 4;
+ waitChar = getCharacterById(listenerId);
+ if (waitChar) {
+ while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
+ doFrame();
+ }
+
+ }
+ int32 talkerId = READ_LE_UINT16(cc - 2);
+
+ waitChar = getCharacterById(talkerId);
+ if (waitChar && !_gameState->_inInventory) {
+ while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
+ doFrame();
+ }
+ } else {
+ if (_audioManager->voiceStillPlaying())
+ _audioManager->stopCurrentVoice();
+ }
+
+ for (int32 i = 0; i < numParticipants - 1; i++) {
+ // listener
+ int32 listenerId = READ_LE_UINT16(c - 2);
+ int32 listenerAnimId = READ_LE_UINT16(c - 4);
+ if (blocking) playTalkAnimOnCharacter(listenerAnimId, listenerId, false);
+ c -= 4;
+ }
+
+ int32 talkerId = READ_LE_UINT16(c - 2);
+ int32 talkerAnimId = READ_LE_UINT16(c - 4);
+
+ _currentTextLine = myLine;
+ _currentTextLineCharacterId = talkerId;
+ _currentTextLineId = dialogid;
+
+ if (blocking) {
+ playTalkAnimOnCharacter(talkerAnimId, talkerId, true);
+ } else {
+ Character *character = getCharacterById(talkerId);
+ if (character)
+ character->stopSpecialAnim();
+ }
+
+ debugC(0, 0xfff, "Talker = %d (num participants : %d) will say '%s'", talkerId , numParticipants, myLine);
+
+
+ getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY);
+
+ if (dialogid < 1000) {
+ myId = _roomTexts->getId(dialogid);
+ _audioManager->playVoice(myId, false);
+ } else {
+ myId = _genericTexts->getId(dialogid - 1000);
+ _audioManager->playVoice(myId, true);
+ }
+
+ if (blocking) {
+ while (_audioManager->voiceStillPlaying() && !_shouldQuit)
+ doFrame();
+ _gameState->_mouseHidden = oldMouseHidden && _gameState->_mouseHidden;
+ }
+
+
+ return 1;
+}
+
+void ToonEngine::haveAConversation(int32 convId) {
+ setCursor(0);
+
+ _gameState->_inConversation = true;
+ _gameState->_showConversationIcons = false;
+ _gameState->_exitConversation = false;
+ _gameState->_sackVisible = false;
+ Conversation *conv = &state()->_conversationState[convId];
+ _gameState->_currentConversationId = convId;
+
+ // change the music to the "conversation" music if needed.
+ playRoomMusic();
+
+ if (conv->_enable) {
+ // fix dialog script based on new flags
+ for (int32 i = 0; i < 10; i++) {
+ if (conv->state[i]._data2 == 1 || conv->state[i]._data2 == 3) {
+ if (getConversationFlag(_gameState->_currentScene, conv->state[i]._data3))
+ conv->state[i]._data2 = 1;
+ else
+ conv->state[i]._data2 = 3;
+ }
+ }
+
+ // if current voice stream sub 15130
+ processConversationClick(conv , 2);
+ doFrame();
+ }
+
+
+ _mouseButton = 0;
+ _gameState->_firstConverstationLine = true;
+
+ while (!_gameState->_exitConversation && !_shouldQuit) {
+ _gameState->_mouseHidden = false;
+ _gameState->_showConversationIcons = true;
+ int32 oldMouseButton = _mouseButton;
+ while (!_shouldQuit) {
+ doFrame();
+
+ if (_mouseButton != 0) {
+ if (!oldMouseButton)
+ break;
+ } else {
+ oldMouseButton = 0;
+ }
+ }
+ int selected = -1;
+ int a = 0;
+ for (int i = 0; i < 10; i++) {
+ if (conv->state[i]._data2 == 1) {
+ if (_mouseX > 50 + a * 60 && _mouseX < 100 + a * 60 && _mouseY >= 336 && _mouseY <= 386) {
+ selected = i;
+ break;
+ }
+ a++;
+ }
+ }
+ if (_shouldQuit) return;
+ _gameState->_showConversationIcons = false;
+ _gameState->_mouseHidden = 1;
+
+
+ if (selected < 0 || selected == 1 || selected == 3) {
+ if (_gameState->_firstConverstationLine)
+ processConversationClick(conv, 3);
+ else
+ processConversationClick(conv, 1);
+ break;
+ } else {
+ processConversationClick(conv, selected);
+ }
+ }
+
+// Strangerke - Commented (not used)
+// int cur = 0;
+
+ for (int i = 0; i < 10; i++) {
+ if (conv->state[i]._data2 == 2) {
+ if (i != 3)
+ conv->state[i]._data2 = 1;
+ }
+ }
+
+ _gameState->_exitConversation = false;
+ _gameState->_inConversation = false;
+ _gameState->_currentConversationId = -1;
+ _gameState->_mouseHidden = false;
+ _gameState->_sackVisible = true;
+
+ // switch back to original music
+ playRoomMusic();
+
+}
+
+void ToonEngine::drawConversationIcons() {
+ if (!_gameState->_inConversation || !_gameState->_showConversationIcons)
+ return;
+ int32 aa = 50 + _gameState->_currentScrollValue;
+ for (int32 i = 0; i < 10; i++) {
+ if (_gameState->_conversationState[_gameState->_currentConversationId].state[i]._data2 == 1) {
+ _dialogIcons->drawFrame(*_mainSurface, (i + _gameState->_currentScene) & 7, aa, 336);
+ _dialogIcons->drawFrame(*_mainSurface, 7 + _gameState->_conversationState[_gameState->_currentConversationId].state[i]._data3, aa, 339);
+ aa += 60;
+ }
+ }
+}
+
+void ToonEngine::prepareConversations() {
+ Conversation *allConvs = _gameState->_conversationState;
+ for (int32 i = 0; i < 60; i++) {
+
+ allConvs[i].state[0]._data2 = 1;
+ if (!allConvs[i].state[0]._data3) {
+ allConvs[i].state[0]._data3 = 1;
+ }
+ allConvs[i].state[1]._data2 = 1;
+ allConvs[i].state[1]._data3 = 6;
+ allConvs[i].state[3]._data2 = 2;
+
+ }
+ int numConversations = READ_LE_UINT16(_conversationData + 1);
+ int16 *curConversation = _conversationData + 3;
+ for (int i = 0; i < numConversations; i++) {
+ Conversation *conv = &allConvs[ READ_LE_UINT16(curConversation)];
+ if (!conv->_enable) {
+
+ conv->_enable = 1;
+
+ int16 offset1 = READ_LE_UINT16(curConversation + 1);
+ void *convData1 = (char *)_conversationData + offset1;
+ conv->state[0]._data4 = convData1;
+
+ int16 offset2 = READ_LE_UINT16(curConversation + 2);
+ void *convData2 = (char *)_conversationData + offset2;
+ conv->state[1]._data4 = convData2;
+
+ int16 offset3 = READ_LE_UINT16(curConversation + 3);
+ void *convData3 = (char *)_conversationData + offset3;
+ conv->state[2]._data4 = convData3;
+
+ int16 offset4 = READ_LE_UINT16(curConversation + 4);
+ void *convData4 = (char *)_conversationData + offset4;
+ conv->state[3]._data4 = convData4;
+ }
+ curConversation += 5;
+ }
+}
+
+void ToonEngine::processConversationClick(Conversation *conv, int32 status) {
+ Conversation::ConvState *v2 = (Conversation::ConvState *)&conv->state[status];
+
+ int16 *i = (int16 *)((char *)v2->_data4 + 2);
+
+ _gameState->_firstConverstationLine = false;
+ while (*i >= 0) {
+ if (*i < 100) {
+ if (_gameState->_exitConversation == false) {
+ characterTalk(i[1]);
+ }
+ } else {
+ runConversationCommand(&i);
+ }
+ i += 2;
+ }
+
+ int16 command = i[0];
+ int16 value = i[1];
+
+ if (command == -1) {
+ v2->_data2 = 0;
+ } else if (command == -2) {
+ v2->_data4 = (char *)_conversationData + value;
+ v2->_data3 = *(int16 *)v2->_data4;
+ } else if (command == -3) {
+ v2->_data2 = 2;
+ v2->_data4 = (char *)_conversationData + value;
+ v2->_data3 = *(int16 *)v2->_data4;
+ }
+
+ int16 *v7 = i + 2;
+// Strangerke - Commented (not used)
+// int16 v6 = conv->state[0].data2;
+ int16 v8 = *v7;
+ if (v8 == -1) {
+ _gameState->_mouseHidden = false;
+ } else {
+retry:
+ while (1) {
+ v7 += 1;
+ int16 *v14 = (int16 *)((char *)_conversationData + v8);
+
+ // find free dialogue slot
+ for (int j = 0; j < 10; j++) {
+ if (!conv->state[j]._data2) {
+ conv->state[j]._data3 = *v14;
+ conv->state[j]._data4 = v14;
+ if (getConversationFlag(_gameState->_currentScene, conv->state[j]._data3))
+ conv->state[j]._data2 = 1;
+ else
+ conv->state[j]._data2 = 3;
+
+ v8 = *v7;
+ if (v8 == -1)
+ return;
+
+ goto retry;
+ }
+ }
+
+ if (v8 != -1)
+ continue;
+
+ break;
+ }
+ }
+
+}
+
+// hardcoded conversation flag to know if one dialogue icon must be displayed or not
+// based on game events...
+int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) {
+ if (locationId == 1) {
+ if (param == 0x34)
+ return _gameState->getGameFlag(93);
+
+ if (param != 55)
+ return 1;
+
+ if (!_gameState->getGameFlag(262))
+ return 1;
+
+ return 0;
+ } else if (locationId == 2) {
+ if (param == 36 && _gameState->getGameFlag(149))
+ return 0;
+ return 1;
+ } else if (locationId == 7) {
+ if (param == 30)
+ return _gameState->getGameFlag(132);
+ else
+ return 1;
+ } else if (locationId == 8) {
+ if (param == 0x20) {
+ if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
+ return 1;
+ return 0;
+ }
+ if (param == 33) {
+ if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
+ return 0;
+ return 1;
+ }
+ } else if (locationId == 0xb) {
+ if (param == 0x12) {
+ if (!_gameState->hasItemInInventory(71))
+ return 1;
+ else
+ return 0;
+ }
+ if (param == 74) {
+ if (_gameState->hasItemInInventory(71))
+ return 1;
+ else
+ return 0;
+ }
+ return 1;
+ } else if (locationId == 0xc) {
+ if (param == 0x3d && _gameState->getGameFlag(154)) {
+ return 0;
+ }
+ if (param == 76 && !_gameState->getGameFlag(79)) {
+ return 0;
+ }
+ if (param == 0x4e && !_gameState->hasItemInInventory(32)) {
+ return 0;
+ }
+ if (param == 0x4f && !_gameState->hasItemInInventory(92)) {
+ return 0;
+ }
+ if (param == 80 && !_gameState->hasItemInInventory(91)) {
+ return 0;
+ }
+ if (param == 0x4d && _gameState->getGameFlag(79)) {
+ return 0;
+ }
+ } else if (locationId == 0xd) {
+ if (param == 0x2f && _gameState->getGameFlag(81)) {
+ return 0;
+ }
+ if (param == 48 && _gameState->getGameFlag(81)) {
+ return 0;
+ }
+ } else if (locationId == 0x10) {
+ switch (param) {
+ case 0x3e8:
+ if (!(_gameState->_gameGlobalData[83] & 1))
+ return 0;
+ break;
+ case 0x3e9:
+ if (!(_gameState->_gameGlobalData[83] & 2))
+ return 0;
+ break;
+ case 0x3ea:
+ if (!(_gameState->_gameGlobalData[83] & 4))
+ return 0;
+ break;
+ case 0x3eb:
+ if (!(_gameState->_gameGlobalData[83] & 8))
+ return 0;
+ break;
+ case 0x3ec:
+ if (!(_gameState->_gameGlobalData[83] & 16))
+ return 0;
+ break;
+ case 0x3ed:
+ if (!(_gameState->_gameGlobalData[83] & 32))
+ return 0;
+ break;
+ case 0x3ee:
+ if (!(_gameState->_gameGlobalData[83] & 64))
+ return 0;
+ break;
+ default:
+ break;
+ };
+ return 1;
+ } else if (locationId == 0x12) {
+ if (param == 0x28 && _gameState->getGameFlag(91)) {
+ return 0;
+ }
+ if (param == 41 && (!_gameState->getGameFlag(96) || _gameState->getGameFlag(91))) {
+ return 0;
+ }
+ } else if (locationId == 0x13) {
+ if (param == 0x32 && _gameState->getGameFlag(107)) {
+ return 0;
+ }
+ if (param == 68 && !_gameState->getGameFlag(107)) {
+ return 0;
+ }
+ } else if (locationId == 0x14) {
+ if (param == 1000 && !_gameState->getGameFlag(82)) {
+ return 0;
+ }
+ } else if (locationId == 0x25) {
+ if (param == 7 && _gameState->_gameGlobalData[28] != 1) {
+ return 0;
+ }
+ if (param == 8 && _gameState->_gameGlobalData[28] != 1) {
+ return 0;
+ }
+ if (param == 9 && _gameState->_gameGlobalData[28] != 1) {
+ return 0;
+ }
+ if (param == 75 && _gameState->_gameGlobalData[28] != 2) {
+ return 0;
+ }
+ } else if (locationId == 72) {
+ if (param == 63 && _gameState->getGameFlag(105)) {
+ return 0;
+ }
+ if (param == 67 && !_gameState->getGameFlag(105)) {
+ return 0;
+ }
+ if (param == 0x40 && !_gameState->getGameFlag(105)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int ToonEngine::runConversationCommand(int16 **command) {
+
+// Strangerke - Commented (not used)
+// int16 com = **command;
+ int16 *v5 = *command;
+
+ int v2 = v5[0];
+ int v4 = v5[1];
+ int result = v2 - 100;
+ switch (v2) {
+ case 100:
+ result = runEventScript(_mouseX, _mouseY, 2, v4, 0);
+ break;
+ case 101:
+ _gameState->_exitConversation = true;
+ break;
+ case 102:
+ playSoundWrong();
+ break;
+ case 104:
+ *command = (int16 *)((char *)_conversationData + v4);
+ *command = (int16 *)((char *)_conversationData + v4 - 4);
+ break;
+ //
+ case 105:
+ if (getConversationFlag(_gameState->_currentScene, v4)) {
+ result = *(int16 *)(*command + 4);
+ *command = (int16 *)((char *)_conversationData + result);
+ *command = (int16 *)((char *)_conversationData + result - 4);
+ } else {
+ int16 *newPtr = *command + 1;
+ *command = newPtr;
+ }
+ break;
+ case 103:
+ return result;
+ break;
+ }
+ return result;
+}
+
+int32 ToonEngine::waitTicks(int32 numTicks, bool breakOnMouseClick) {
+// Strangerke - Commented (not used)
+// Common::EventManager *_event = _system->getEventManager();
+
+ uint32 nextTime = _system->getMillis() + numTicks * _tickLength;
+ while (_system->getMillis() < nextTime || numTicks == -1) {
+ //if (!_animationSceneScriptRunFlag)
+ // break;
+ updateAnimationSceneScripts(0);
+ getMouseEvent();
+ simpleUpdate();
+
+ if (breakOnMouseClick && (_mouseButton & 0x2))
+ break;
+ }
+ return 0;
+}
+
+void ToonEngine::renderInventory() {
+ if (!_gameState->_inInventory)
+ return;
+
+ _inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0);
+
+ // draw items on screen
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ int32 x = 57 * (i % 7) + 114;
+ int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
+ _inventoryIconSlots->drawFrame(*_mainSurface, i % 12, x + _gameState->_currentScrollValue, y);
+ if (_gameState->_inventory[i])
+ _inventoryIcons->drawFrame(*_mainSurface, _gameState->_inventory[i], x + _gameState->_currentScrollValue + 2, y + 2);
+ }
+
+ drawConversationLine();
+ if (!_audioManager->voiceStillPlaying()) {
+ _currentTextLineCharacterId = -1;
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+
+ if (_firstFrame) {
+ copyToVirtualScreen(false);
+ _firstFrame = false;
+ fadeIn(5);
+ }
+ copyToVirtualScreen();
+}
+
+int32 ToonEngine::showInventory() {
+ int32 oldScrollValue = _gameState->_currentScrollValue;
+// Strangerke - Commented (not used)
+// Common::EventManager *_event = _system->getEventManager();
+ _inventoryPicture = new Picture(this);
+ fadeOut(5);
+ _inventoryPicture->loadPicture("SACK128.CPS", true);
+ _inventoryPicture->setupPalette();
+
+ if (_gameState->_mouseState >= 0) {
+ setCursor(_gameState->_mouseState, true, -18, -14);
+
+ // make sure we have a free spot
+ if (!_gameState->hasItemInInventory(0)) {
+ _gameState->_inventory[_gameState->_numInventoryItems] = 0;
+ _gameState->_numInventoryItems++;
+ }
+ } else {
+ setCursor(0);
+ }
+
+ _gameState->_inInventory = true;
+ _gameState->_currentScrollValue = 0;
+
+ int32 oldMouseButton = 0;
+ int32 justPressedButton = 0;
+ _firstFrame = true;
+
+ while (!_shouldQuit) {
+ getMouseEvent();
+
+ justPressedButton = _mouseButton & ~oldMouseButton;
+ oldMouseButton = _mouseButton;
+
+ if (justPressedButton & 0x3) {
+ // find out what object we're on
+ int32 foundObj = -1;
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ int32 x = 57 * (i % 7) + 114;
+ int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
+ if (_mouseX >= (_gameState->_currentScrollValue + x - 6) &&
+ _mouseX <= (_gameState->_currentScrollValue + x + 44 + 7) &&
+ _mouseY >= y - 6 && _mouseY <= y + 50) {
+ foundObj = i;
+ break;
+ }
+ }
+
+ if (justPressedButton & 0x1) {
+ if (_gameState->_mouseState < 0) {
+ if (foundObj >= 0) {
+ // take an object
+ int32 item = _gameState->_inventory[foundObj];
+
+ int32 modItem = getSpecialInventoryItem(item);
+ if (modItem) {
+
+ if (modItem == -1) {
+ _gameState->_mouseState = item;
+ _gameState->_inventory[foundObj] = 0;
+ } else {
+ _gameState->_mouseState = modItem;
+ if (!_gameState->hasItemInInventory(0)) {
+ _gameState->_inventory[_gameState->_numInventoryItems] = 0;
+ _gameState->_numInventoryItems++;
+ }
+ }
+
+ setCursor(_gameState->_mouseState, true, -18, -14);
+ }
+
+ } else {
+ break;
+ }
+ } else {
+ if (foundObj >= 0 && _gameState->_inventory[foundObj] == 0) { // empty place
+ _gameState->_inventory[foundObj] = _gameState->_mouseState;
+ setCursor(0, false);
+ _gameState->_mouseState = -1;
+ } else if (foundObj >= 0 && _gameState->_inventory[foundObj] > 0) {
+ if (!handleInventoryOnInventory(_gameState->_mouseState, _gameState->_inventory[foundObj]))
+ playSoundWrong();
+ } else {
+ // quit the inventory mode with the icon
+ break;
+ }
+ }
+
+ } else if (justPressedButton & 0x2) { // right button
+ if (foundObj >= 0) {
+ // talk about the object
+ if (!handleInventoryOnInventory(_gameState->_inventory[foundObj], -1))
+ characterTalk(1000 + _gameState->_inventory[foundObj]);
+ } else {
+ // go out
+ break;
+ }
+ }
+ }
+
+ renderInventory();
+
+ }
+
+ _gameState->_currentScrollValue = oldScrollValue;
+ _gameState->_inInventory = false;
+
+ fadeOut(5);
+ if (_gameState->_inCloseUp) {
+ _gameState->_inCloseUp = false;
+ flipScreens();
+ } else if (_gameState->_inCutaway) {
+ _currentCutaway->setupPalette();
+ setupGeneralPalette();
+ } else {
+ _currentPicture->setupPalette();
+ setupGeneralPalette();
+ }
+ flushPalette();
+ _firstFrame = true;
+
+ return 0;
+}
+
+void ToonEngine::getMouseEvent() {
+ Common::EventManager *_event = _system->getEventManager();
+
+ Common::Event event;
+ while (_event->pollEvent(event) && !_shouldQuit)
+ ;
+
+ _mouseX = _event->getMousePos().x;
+ _mouseY = _event->getMousePos().y;
+ _mouseButton = _event->getButtonState();
+}
+
+void ToonEngine::drawSack() {
+ if (_gameState->_sackVisible) {
+ _inventoryIcons->drawFrame(*_mainSurface, 0, _gameState->_currentScrollValue, 356);
+ }
+}
+
+void ToonEngine::addItemToInventory(int32 item) {
+
+ if (item == 103 || item == 104 || item == 89 || item == 82) {
+ // can't add that to inventory
+ _gameState->_mouseState = -1;
+ return;
+ }
+
+ if (item == 41) {
+ // confiscated inventory
+ for (int32 i = 0; i < _gameState->_numConfiscatedInventoryItems; i++)
+ addItemToInventory(_gameState->_confiscatedInventory[i]);
+
+ _gameState->_numConfiscatedInventoryItems = 0;
+ _gameState->_mouseState = -1;
+ return;
+ }
+
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ if (_gameState->_inventory[i] == 0) {
+ _gameState->_inventory[i] = item;
+ _gameState->_mouseState = -1;
+ return;
+ }
+ }
+ _gameState->_inventory[_gameState->_numInventoryItems] = item;
+ _gameState->_numInventoryItems++;
+ _gameState->_mouseState = -1;
+}
+
+void ToonEngine::createMouseItem(int32 item) {
+ _gameState->_mouseState = item;
+ setCursor(_gameState->_mouseState, true, -18, -14);
+}
+
+void ToonEngine::deleteMouseItem() {
+ _gameState->_mouseState = -1;
+ rearrangeInventory();
+ setCursor(0);
+}
+
+void ToonEngine::showCutaway(Common::String cutawayPicture) {
+ _gameState->_inCutaway = true;
+ _currentCutaway = new Picture(this);
+ if (cutawayPicture == "") {
+ cutawayPicture = Common::String(_gameState->_locations[_gameState->_currentScene]._cutaway) + ".CPS";
+ }
+ _currentCutaway->loadPicture(cutawayPicture, false);
+ _currentCutaway->setupPalette();
+ _oldScrollValue = _gameState->_currentScrollValue;
+ _gameState->_currentScrollValue = 0;
+ flushPalette();
+}
+
+void ToonEngine::hideCutaway() {
+ _gameState->_inCutaway = false;
+ _gameState->_sackVisible = true;
+ delete _currentCutaway;
+ _gameState->_currentScrollValue = _oldScrollValue;
+ _currentCutaway = 0;
+ _currentPicture->setupPalette();
+ flushPalette();
+}
+
+void ToonEngine::updateCharacters(int32 timeElapsed) {
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i]) {
+ _characters[i]->update(timeElapsed);
+ }
+ }
+}
+
+void ToonEngine::drawPalette() {
+ for (int32 i = 0; i < 256; i++) {
+ int32 x = i % 32;
+ int32 y = i / 32;
+ _mainSurface->fillRect(Common::Rect(x * 16, y * 16, x * 16 + 16, y * 16 + 16), i);
+ }
+}
+
+void ToonEngine::rearrangeInventory() {
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ if (_gameState->_inventory[i] == 0) {
+ // move all the following items from one
+ for (int32 j = i + 1; j < _gameState->_numInventoryItems; j++) {
+ _gameState->_inventory[j-1] = _gameState->_inventory[j];
+ }
+ _gameState->_numInventoryItems--;
+ }
+ }
+}
+
+void ToonEngine::newGame() {
+
+ if (_isDemo) {
+ addItemToInventory(59);
+ addItemToInventory(67);
+ addItemToInventory(11);
+ addItemToInventory(19);
+ loadScene(_gameState->_currentScene);
+ } else {
+ //loadScene(4);
+ loadScene(_gameState->_currentScene);
+ }
+}
+
+void ToonEngine::playSFX(int32 id, int32 volume) {
+ if (id < 0)
+ _audioManager->playSFX(-id + 1, volume, true);
+ else
+ _audioManager->playSFX(id , volume, false);
+}
+
+void ToonEngine::playSoundWrong() {
+ _audioManager->playSFX(rand() & 7, 128, true);
+}
+
+void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) {
+
+ if (characterId < 0)
+ characterId = 0;
+
+ // default position is the center of current screen
+ *retX = _gameState->_currentScrollValue + 320;
+ *retY = 70;
+
+ // hardcoded special cases...
+ if (characterId == 0) {
+ // drew
+ int32 x = _drew->getX();
+ int32 y = _drew->getY();
+ if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) {
+ if (!_gameState->_inCutaway && !_gameState->_inInventory) {
+ *retX = x;
+ *retY = y - ((_drew->getScale() * 256 / 1024) >> 1) - 45;
+ }
+ }
+ } else if (characterId == 1) {
+ // flux
+ int32 x = _flux->getX();
+ int32 y = _flux->getY();
+ if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + 640) {
+ if (!_gameState->_inCutaway) {
+ *retX = x;
+ *retY = y - ((_drew->getScale() * 100 / 1024) >> 1) - 30;
+ }
+ }
+ } else if (characterId == 5 || characterId == 39) {
+ *retX = 80;
+ *retY = 120;
+ } else if (characterId == 14) {
+ *retX = 257;
+ *retY = 132;
+ } else if (characterId == 18) {
+ *retX = 80;
+ *retY = 180;
+ } else if (characterId == 21) {
+ *retX = 363;
+ *retY = 193;
+ } else if (characterId == 23) {
+ *retX = 532;
+ *retY = 178;
+ } else if (characterId == 33) {
+ *retX = 167;
+ *retY = 172;
+ } else {
+
+ // more "standard" code by character
+ Character *character = getCharacterById(characterId);
+ if (character && !_gameState->_inCutaway) {
+ if (character->getAnimationInstance()) {
+ if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + 640) {
+ int32 x1, y1, x2, y2;
+ character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
+ *retX = (x1 + x2) / 2;
+ *retY = y1;
+ }
+ }
+ }
+ }
+}
+
+Character *ToonEngine::getCharacterById(int32 charId) {
+
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i] && _characters[i]->getId() == charId)
+ return _characters[i];
+ }
+ return 0;
+}
+
+void ToonEngine::drawConversationLine() {
+ if (_currentTextLine) {
+ _fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
+ _fontRenderer->setFont(_fontToon);
+ _fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, Common::String(_currentTextLine), 0);
+ }
+}
+
+Common::String ToonEngine::getSavegameName(int nr) {
+ return _targetName + Common::String::printf(".%03d", nr);
+}
+
+bool ToonEngine::saveGame(int32 slot) {
+ const EnginePlugin *plugin = NULL;
+ int16 savegameId;
+ Common::String savegameDescription;
+ EngineMan.findGame(_gameDescription->gameid, &plugin);
+
+ if (slot == -1) {
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ dialog->setSaveMode(true);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ savegameDescription = dialog->getResultString();
+ delete dialog;
+ } else {
+ savegameId = slot;
+ savegameDescription = Common::String::printf("Quick save #%d", slot);
+ }
+
+ if (savegameId < 0)
+ return false; // dialog aborted
+
+ Common::String savegameFile = getSavegameName(savegameId);
+ Common::SaveFileManager *saveMan = g_system->getSavefileManager();
+ Common::OutSaveFile *saveFile = saveMan->openForSaving(savegameFile);
+ if (!saveFile)
+ return false;
+
+ // save savegame header
+ saveFile->writeSint32BE(TOON_SAVEGAME_VERSION);
+
+ if (savegameDescription == "") {
+ savegameDescription = "Untitled savegame";
+ }
+
+ saveFile->writeSint16BE(savegameDescription.size() + 1);
+ saveFile->write(savegameDescription.c_str(), savegameDescription.size() + 1);
+
+ Graphics::saveThumbnail(*saveFile);
+
+ TimeDate curTime;
+ _system->getTimeAndDate(curTime);
+
+ uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF);
+ uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF);
+
+ saveFile->writeUint32BE(saveDate);
+ saveFile->writeUint16BE(saveTime);
+
+
+ // save global state
+ _gameState->save(saveFile);
+ _gameState->saveConversations(saveFile);
+ _hotspots->save(saveFile);
+
+ // save current time to be able to patch the time when loading
+ saveFile->writeSint32BE(getOldMilli());
+
+ // save script states
+ for (int32 i = 0; i < 4; i++) {
+ _script->saveState(&_scriptState[i], saveFile);
+ }
+
+ // save animation script states
+ for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
+ saveFile->writeByte(_sceneAnimationScripts[i]._active);
+ saveFile->writeByte(_sceneAnimationScripts[i]._frozen);
+ saveFile->writeSint32BE(_sceneAnimationScripts[i]._lastTimer);
+ _script->saveState(&_sceneAnimationScripts[i]._state, saveFile);
+ }
+
+ // save scene animations
+ for (int32 i = 0; i < 64; i++) {
+ _sceneAnimations[i].save(this, saveFile);
+ }
+
+
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i]) {
+ saveFile->writeSByte(i);
+ _characters[i]->save(saveFile);
+ }
+ }
+ saveFile->writeSByte(-1);
+
+ // save "command buffer"
+ saveFile->writeSint16BE(_saveBufferStream->pos());
+ if (_saveBufferStream->pos() > 0) {
+ saveFile->write(_saveBufferStream->getData(), _saveBufferStream->pos());
+ saveFile->writeSint16BE(0);
+ }
+
+ delete saveFile;
+
+ return true;
+}
+
+bool ToonEngine::loadGame(int32 slot) {
+ const EnginePlugin *plugin = NULL;
+ int16 savegameId;
+ EngineMan.findGame(_gameDescription->gameid, &plugin);
+
+ if (slot == -1) {
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore");
+ dialog->setSaveMode(false);
+ savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName());
+ delete dialog;
+ } else {
+ savegameId = slot;
+ }
+ if (savegameId < 0)
+ return false; // dialog aborted
+
+ Common::String savegameFile = getSavegameName(savegameId);
+ Common::SaveFileManager *saveMan = g_system->getSavefileManager();
+ Common::InSaveFile *loadFile = saveMan->openForLoading(savegameFile);
+ if (!loadFile)
+ return false;
+
+ int32 saveGameVersion = loadFile->readSint32BE();
+ if (saveGameVersion != TOON_SAVEGAME_VERSION) {
+ delete loadFile;
+ return false;
+ }
+ int32 saveGameNameSize = loadFile->readSint16BE();
+ loadFile->skip(saveGameNameSize);
+
+ // We don't need the thumbnail here, so just read it and discard it
+ Graphics::skipThumbnail(*loadFile);
+
+ loadFile->skip(6); // date & time skip
+
+ if (_gameState->_currentScene != -1) {
+ exitScene();
+ }
+
+
+ _gameState->load(loadFile);
+ loadScene(_gameState->_currentScene, true);
+ _gameState->loadConversations(loadFile);
+ _hotspots->load(loadFile);
+
+ // read the old time
+ int32 savedTime = loadFile->readSint32BE();
+ int32 timerDiff = _system->getMillis() - savedTime;
+
+ // load script states
+ for (int32 i = 0; i < 4; i++) {
+ _script->loadState(&_scriptState[i], loadFile);
+ }
+
+ // load animation script states
+ for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
+ _sceneAnimationScripts[i]._active = loadFile->readByte();
+ _sceneAnimationScripts[i]._frozen = loadFile->readByte();
+ int32 oldTimer = loadFile->readSint32BE();
+ _sceneAnimationScripts[i]._lastTimer = MAX(0,oldTimer + timerDiff);
+ _script->loadState(&_sceneAnimationScripts[i]._state, loadFile);
+ }
+
+ // load scene animations
+ for (int32 i = 0; i < 64; i++) {
+ _sceneAnimations[i].load(this, loadFile);
+ }
+
+ _gameState->_timerTimeout[0] += timerDiff;
+ _gameState->_timerTimeout[1] += timerDiff;
+
+ /*
+ int32 diff = _conversationData - _gameState->_conversationData;
+
+ for (int32 i = 0; i < 60; i++) {
+ if (_gameState->_conversationState[i]._enable) {
+ // we have to fix up our pointers...
+ for (int32 a = 0; a < 10; a++) {
+ if (_gameState->_conversationState[i].state[a]._data4)
+ _gameState->_conversationState[i].state[a]._data4 = (int16 *)_gameState->_conversationState[i].state[a]._data4 + diff;
+ }
+ }
+ }
+ */
+
+ _gameState->_conversationData = _conversationData;
+ _firstFrame = true;
+
+ // read characters info
+ while (1) {
+ int8 c = loadFile->readSByte();
+ if (c < 0)
+ break;
+
+ if (!_characters[c]) {
+ _characters[c] = new Character(this);
+ }
+ _characters[c]->load(loadFile);
+ //_characters[c]->setVisible(true);
+ _characters[c]->update(0);
+ }
+
+ // load "command buffer"
+ int32 size = loadFile->readSint16BE();
+ if (size) {
+ uint8 *buf = new uint8[size+2];
+ loadFile->read(buf, size + 2);
+
+ Common::MemoryReadStream rStr(buf, size + 2);
+ while (1) {
+ int16 command = rStr.readSint16BE();
+ if (!command) break;
+ switch (command) {
+ case 1: {
+ int16 frame = rStr.readSint16BE();
+ int16 animLen = rStr.readSint16BE();
+ char animName[32];
+ rStr.read(animName, animLen);
+ int16 x = rStr.readSint16BE();
+ int16 y = rStr.readSint16BE();
+// int16 z = rStr.readSint16BE();
+// int16 layerZ = rStr.readSint16BE();
+ rStr.readSint16BE();
+ rStr.readSint16BE();
+
+ Animation *anim = new Animation(this);
+ anim->loadAnimation(animName);
+ anim->drawFrameOnPicture(frame, x, y);
+ delete anim;
+ break;
+ }
+ case 2: {
+ int16 x = rStr.readSint16BE();
+ int16 y = rStr.readSint16BE();
+ int16 x1 = rStr.readSint16BE();
+ int16 y1 = rStr.readSint16BE();
+ makeLineNonWalkable(x, y, x1, y1);
+ break;
+ }
+ case 3: {
+ int16 x = rStr.readSint16BE();
+ int16 y = rStr.readSint16BE();
+ int16 x1 = rStr.readSint16BE();
+ int16 y1 = rStr.readSint16BE();
+ makeLineWalkable(x, y, x1, y1);
+ break;
+ }
+ case 4: {
+ int16 x = rStr.readSint16BE();
+ int16 y = rStr.readSint16BE();
+ getMask()->floodFillNotWalkableOnMask(x, y);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ _saveBufferStream->write(buf, size);
+ delete loadFile;
+ }
+ return true;
+}
+
+// another special case for inventory
+int32 ToonEngine::getSpecialInventoryItem(int32 item) {
+
+ // butter
+ if (item == 12) {
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ if (_gameState->_inventory[i] == 12)
+ _gameState->_inventory[i] = 11;
+ }
+ return 11;
+
+ } else if (item == 84) {
+ if (_gameState->getGameFlag(26)) {
+ characterTalk(1726);
+ return 0;
+ } else {
+ if (!_gameState->hasItemInInventory(102) && !_gameState->hasItemInInventory(90) && !_gameState->hasItemInInventory(89)) {
+ characterTalk(1416);
+ return 102;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void ToonEngine::initCharacter(int32 characterId, int32 animScriptId, int32 sceneAnimationId, int32 animToPlayId) {
+ // find a new index
+ int32 characterIndex = -1;
+ for (int32 i = 0; i < 8; i++) {
+ if (_characters[i] && _characters[i]->getId() == characterId) {
+ characterIndex = i;
+ break;
+ }
+
+ if (!_characters[i]) {
+ characterIndex = i;
+ break;
+ }
+ }
+
+ if (characterIndex == -1) {
+ return;
+ }
+
+// Strangerke - Commented (not used)
+// if (_characters[characterIndex])
+// delete current char
+
+ _characters[characterIndex] = new Character(this);
+ _characters[characterIndex]->setId(characterId);
+ _characters[characterIndex]->setAnimScript(animScriptId);
+ _characters[characterIndex]->setDefaultSpecialAnimationId(animToPlayId);
+ _characters[characterIndex]->setSceneAnimationId(sceneAnimationId);
+ _characters[characterIndex]->setFlag(0);
+ _characters[characterIndex]->setVisible(true);
+ if (sceneAnimationId != -1)
+ _characters[characterIndex]->setAnimationInstance(_sceneAnimations[sceneAnimationId]._animInstance);
+}
+
+int32 ToonEngine::handleInventoryOnFlux(int32 itemId) {
+
+ switch (itemId) {
+ case 8:
+ sayLines(1, 1332);
+ break;
+ case 0x14:
+ case 0x15:
+ case 0x45:
+ sayLines(1, 1304);
+ break;
+ case 0x68:
+ _gameState->_mouseState = 0;
+ setCursor(0, false, 0, 0);
+ break;
+ case 116:
+ sayLines(1, 1306);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void ToonEngine::storePalette() {
+ memcpy(_backupPalette, _finalPalette, 768);
+}
+
+void ToonEngine::restorePalette() {
+ memcpy(_finalPalette, _backupPalette, 768);
+ flushPalette();
+}
+
+const char *ToonEngine::getSpecialConversationMusic(int32 conversationId) {
+ static const char * const specialMusic[] = {
+ 0, 0,
+ "BR091013", "BR091013",
+ "NET1214", "NET1214",
+ 0, 0,
+ "CAR1365B", "CAR1365B",
+ 0, 0,
+ 0, 0,
+ "CAR14431", "CAR14431",
+ 0, 0,
+ 0, 0,
+ "SCD16520", "SCD16520",
+ "SCD16520", "SCD16520",
+ "SCD16522", "SCD16522",
+ 0, 0,
+ "KPM8719", "KPM8719",
+ 0, 0,
+ "CAR1368B", "CAR1368B",
+ 0, 0,
+ 0, 0,
+ "KPM6337", "KPM6337",
+ "CAR20471", "CAR20471",
+ "CAR136_1", "KPM87_57",
+ 0, 0,
+ "CAR13648", "CAR13648",
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ "SCD16526", "SCD16526",
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0
+ };
+
+ return specialMusic[randRange(0, 1) + conversationId * 2];
+}
+
+void ToonEngine::viewInventoryItem(Common::String str, int32 lineId, int32 itemDest) {
+ storePalette();
+ fadeOut(5);
+
+ Picture *pic = new Picture(this);
+ pic->loadPicture(str, false);
+ pic->setupPalette();
+ flushPalette();
+
+ if (lineId) {
+ characterTalk(lineId, false);
+ }
+
+ uint32 oldMouseButton = _mouseButton;
+ uint32 justPressedButton = 0;
+ _firstFrame = true;
+
+ int32 oldScrollValue = _gameState->_currentScrollValue;
+ _gameState->_currentScrollValue = 0;
+
+ while (!_shouldQuit) {
+ getMouseEvent();
+
+ justPressedButton = _mouseButton & ~oldMouseButton;
+ oldMouseButton = _mouseButton;
+
+ if (justPressedButton) {
+ break;
+ }
+
+ pic->draw(*_mainSurface, 0, 0, 0, 0);
+
+ drawConversationLine();
+ if (!_audioManager->voiceStillPlaying()) {
+ _currentTextLineCharacterId = -1;
+ _currentTextLine = 0;
+ _currentTextLineId = -1;
+ }
+
+ if (_firstFrame) {
+ copyToVirtualScreen(false);
+ _firstFrame = false;
+ fadeIn(5);
+ }
+
+ copyToVirtualScreen();
+ }
+
+ fadeOut(5);
+ restorePalette();
+ _firstFrame = true;
+ _gameState->_currentScrollValue = oldScrollValue;
+ delete pic;
+
+}
+
+int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) {
+ switch (itemDest) {
+ case 0:
+ return handleInventoryOnDrew(itemSrc);
+ case 1:
+ if (itemSrc == 71) {
+ sayLines(2, 1212);
+ return 1;
+ }
+ break;
+ case 5:
+ if (itemSrc == 15) {
+ characterTalk(1492);
+ } else if (itemSrc == 0x2f) {
+ characterTalk(1488);
+ } else if (itemSrc == 88) {
+ sayLines(2, 1478);
+ } else {
+ return 0;
+ }
+ break;
+ case 6:
+ if (itemSrc == -1) {
+ viewInventoryItem("BLUEPRNT.CPS", 1006, itemDest);
+ return 1;
+ } else
+ return 0;
+ break;
+ case 8:
+ if (itemSrc == -1) {
+ viewInventoryItem("BOOK.CPS", 0, itemDest);
+ return 1;
+ } else {
+ return 0;
+ }
+ break;
+ case 11:
+ if (itemSrc == 0xb) {
+ _gameState->_mouseState = -1;
+ replaceItemFromInventory(11,12);
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ //
+ } else if (itemSrc == 24) {
+ characterTalk(1244);
+ return 1;
+ } else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
+ sayLines(2, 1212);
+ return 1;
+ }
+ break;
+ case 12:
+ if (itemSrc == 24) {
+ characterTalk(1244);
+ return 1;
+ } else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
+ sayLines(2, 1212);
+ return 1;
+ }
+ break;
+ case 13:
+ if (itemSrc == 0x35 || itemSrc == 0x36) {
+ characterTalk(1204);
+ return 1;
+ } else if (itemSrc >= 0x6b && itemSrc <= 0x72) {
+ characterTalk(1312);
+ return 1;
+ }
+ break;
+ case 14:
+ if (itemSrc == -1) {
+ deleteItemFromInventory(14);
+ addItemToInventory(15);
+ addItemToInventory(42);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 43) {
+ characterTalk(1410);
+ return 1;
+ } else if (itemSrc == 49) {
+ characterTalk(1409);
+ return 1;
+ }
+ break;
+ case 16:
+ if (itemSrc == 55) {
+ characterTalk(1400);
+ replaceItemFromInventory(55, 98);
+ return 1;
+ }
+ break;
+ case 19:
+ if (itemSrc == 0x34) {
+ characterTalk(1322);
+ return 1;
+ } else if (itemSrc == 107) {
+ sayLines(2 , 1300);
+ replaceItemFromInventory(107, 111);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x6c) {
+ sayLines(2, 1300);
+ replaceItemFromInventory(108, 112);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x6d) {
+ sayLines(2, 1300);
+ replaceItemFromInventory(109, 113);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 110) {
+ sayLines(2, 1300);
+ replaceItemFromInventory(110, 114);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 20:
+ if (itemSrc == 35) {
+ createMouseItem(21);
+ replaceItemFromInventory(35, 36);
+ return 1;
+ } else if (itemSrc == 0x24) {
+ createMouseItem(21);
+ replaceItemFromInventory(36, 37);
+ return 1;
+ } else if (itemSrc == 37) {
+ deleteItemFromInventory(37);
+ createMouseItem(21);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x6b || itemSrc == 0x6c || itemSrc == 0x6f || itemSrc == 108 || itemSrc == 112) {
+ sayLines(2, 1292);
+ return 1;
+ }
+ break;
+ case 21:
+ switch (itemSrc) {
+
+ case 107:
+ characterTalk(1296);
+ replaceItemFromInventory(107, 109);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 108:
+ characterTalk(1298);
+ replaceItemFromInventory(108, 110);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 111:
+ characterTalk(1296);
+ replaceItemFromInventory(111, 113);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 112:
+ characterTalk(1298);
+ replaceItemFromInventory(112, 114);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 22:
+ if (itemSrc == 32) {
+ characterTalk(1252);
+ return 1;
+ }
+ break;
+ case 24:
+ if (itemSrc == 0xc) {
+ characterTalk(1244);
+ return 1;
+ } else if (itemSrc == 79) {
+ characterTalk(1280);
+ return 1;
+ }
+ break;
+ case 26:
+ if (itemSrc == 0x5e) {
+ characterTalk(1316);
+ return 1;
+ } else if (itemSrc == 95) {
+ characterTalk(1320);
+ return 1;
+ }
+ break;
+ case 31:
+ if (itemSrc == 61) {
+ characterTalk(1412);
+ deleteItemFromInventory(61);
+ createMouseItem(62);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 32:
+ if (itemSrc == 22) {
+ characterTalk(1252);
+ return 1;
+ }
+ break;
+ case 33:
+ if (itemSrc == 117) {
+ characterTalk(1490);
+ return 1;
+ }
+ break;
+ case 34:
+ if (itemSrc == 61) {
+ characterTalk(1414);
+ return 1;
+ }
+ break;
+ case 35:
+ if (itemSrc == -1) {
+ characterTalk(1035);
+ return 1;
+ } else if (itemSrc == 20) {
+ replaceItemFromInventory(20, 21);
+ createMouseItem(36);
+ return 1;
+ } else if (itemSrc == 68) {
+ replaceItemFromInventory(68, 69);
+ createMouseItem(36);
+ return 1;
+ } else if (itemSrc >= 107 && itemSrc <= 114) {
+ characterTalk(1314);
+ return 1;
+ } else {
+ characterTalk(1208);
+ return 1;
+ }
+ break;
+ case 36:
+ if (itemSrc == -1) {
+ characterTalk(1035);
+ return 1;
+ } else if (itemSrc == 20) {
+ replaceItemFromInventory(20, 21);
+ createMouseItem(37);
+ return 1;
+ } else if (itemSrc == 68) {
+ replaceItemFromInventory(68, 69);
+ createMouseItem(37);
+ return 1;
+ } else if (itemSrc >= 107 && itemSrc <= 114) {
+ characterTalk(1314);
+ return 1;
+ } else {
+ characterTalk(1208);
+ return 1;
+ }
+ break;
+ case 37:
+ if (itemSrc == -1) {
+ characterTalk(1035);
+ return 1;
+ } else if (itemSrc == 20) {
+ replaceItemFromInventory(20, 21);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 68) {
+ replaceItemFromInventory(68, 69);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc >= 107 && itemSrc <= 114) {
+ characterTalk(1314);
+ return 1;
+ } else {
+ characterTalk(1208);
+ return 1;
+ }
+ break;
+ case 38:
+ if (itemSrc == 15) {
+ characterTalk(1492);
+ return 1;
+ } else if (itemSrc == 0x2f) {
+ characterTalk(1488);
+ return 1;
+ } else if (itemSrc == 88) {
+ sayLines(2, 1478);
+ return 1;
+ }
+ break;
+ case 40:
+ if (itemSrc == 53) {
+ replaceItemFromInventory(53, 54);
+ characterTalk(1222);
+ return 1;
+ } else if (itemSrc == 0x36) {
+ characterTalk(1228);
+ return 1;
+ } else if (itemSrc == 0x5b) {
+ characterTalk(1230);
+ return 1;
+ } else if (itemSrc == 92) {
+ characterTalk(1220);
+ return 1;
+ }
+ break;
+ case 43:
+ if (itemSrc == 14) {
+ characterTalk(1410);
+ return 1;
+ }
+ break;
+ case 47:
+ if (itemSrc == -1)
+ characterTalk(1047);
+ else
+ characterTalk(1488);
+
+ return 1;
+ case 49:
+ if (itemSrc == 0xe) {
+ characterTalk(1409);
+ return 1;
+ } else if (itemSrc == 38 || itemSrc == 5 || itemSrc == 0x42) {
+ characterTalk(1476);
+ return 1;
+ } else if (itemSrc == 0x34) {
+ characterTalk(1260);
+ return 1;
+ } else if (itemSrc == 0x47) {
+ characterTalk(1246);
+ return 1;
+ } else if (itemSrc == 0x36) {
+ sayLines(2, 1324);
+ return 1;
+ }
+ break;
+ case 52:
+ if (itemSrc == 0x13) {
+ characterTalk(1322);
+ return 1;
+ } else if (itemSrc == 94) {
+ characterTalk(1282);
+ return 1;
+ }
+ break;
+ case 53:
+ if (itemSrc == 40) {
+ createMouseItem(54);
+ characterTalk(1222);
+ return 1;
+ } else if (itemSrc == 0x31) {
+ sayLines(2, 1324);
+ return 1;
+ } else if (itemSrc == 0x34) {
+ characterTalk(1310);
+ return 1;
+ } else if (itemSrc == 91) {
+ characterTalk(1218);
+ return 1;
+ }
+
+ break;
+ case 54:
+ if (itemSrc == 40) {
+ characterTalk(1228);
+ return 1;
+ } else if (itemSrc == 0x34) {
+ characterTalk(1310);
+ return 1;
+ } else if (itemSrc == 0x5b) {
+ characterTalk(1226);
+ replaceItemFromInventory(91, 92);
+ return 1;
+ } else if (itemSrc == 92) {
+ characterTalk(1220);
+ return 1;
+ }
+
+ break;
+ case 55:
+ if (itemSrc == 16) {
+ createMouseItem(98);
+ characterTalk(1400);
+ return 1;
+ }
+ break;
+ case 61:
+ if (itemSrc == 0x1f) {
+ characterTalk(1412);
+ deleteItemFromInventory(31);
+ createMouseItem(62);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x21 || itemSrc == 0x22) {
+ characterTalk(1414);
+ return 1;
+ }
+ break;
+ case 64:
+ if (itemSrc == 0xb) {
+ sayLines(2, 1212);
+ return 1;
+ } else if (itemSrc == 0x5e || itemSrc == 0x5f) {
+ characterTalk(1318);
+ return 1;
+ }
+ break;
+ case 66:
+ if (itemSrc == 15) {
+ characterTalk(1492);
+ return 1;
+ } else if (itemSrc == 0x2f) {
+ characterTalk(1488);
+ return 1;
+ } else if (itemSrc == 88) {
+ sayLines(2, 1478);
+ characterTalk(1478);
+ return 1;
+ }
+ break;
+ case 67:
+ if (itemSrc == 79) {
+ sayLines(2, 1212);
+ return 1;
+ }
+ break;
+ case 68:
+ if (itemSrc == 35) {
+ createMouseItem(69);
+ replaceItemFromInventory(35, 36);
+ return 1;
+ } else if (itemSrc == 0x24) {
+ createMouseItem(69);
+ replaceItemFromInventory(36, 37);
+ return 1;
+ } else if (itemSrc == 37) {
+ deleteItemFromInventory(37);
+ createMouseItem(69);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x6b || itemSrc == 113 || itemSrc == 0x6f || itemSrc == 109) {
+ sayLines(2, 1288);
+ return 1;
+ }
+ break;
+ case 69:
+ switch (itemSrc) {
+ case 107:
+ characterTalk(1296);
+ replaceItemFromInventory(107, 108);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 109:
+ characterTalk(1298);
+ replaceItemFromInventory(109, 110);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 111:
+ characterTalk(1296);
+ replaceItemFromInventory(111, 112);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ case 113:
+ characterTalk(1298);
+ replaceItemFromInventory(113, 114);
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 71:
+ if (itemSrc == 0xc || itemSrc == 1 || itemSrc == 0x41 || itemSrc == 67 || itemSrc == 0x4c || itemSrc == 57) {
+ sayLines(2, 1212);
+ return 1;
+ } else if (itemSrc == 79) {
+ characterTalk(1238);
+ return 1;
+ }
+ break;
+ case 79:
+ if (itemSrc == 1 || itemSrc == 67 || itemSrc == 76 || itemSrc == 57 || itemSrc == 0x41) {
+ sayLines(2, 1212);
+ return 1;
+ } else if (itemSrc == 0x18) {
+ characterTalk(1280);
+ return 1;
+ } else if (itemSrc == 0x47) {
+ characterTalk(1238);
+ return 1;
+ }
+ break;
+ case 82:
+ if (itemSrc == 84) {
+ sayLines(2, 1424);
+ return 1;
+ } else if (itemSrc == 0x58) {
+ deleteItemFromInventory(88);
+ createMouseItem(89);
+ rearrangeInventory();
+ characterTalk(1428);
+ return 1;
+ } else if (itemSrc == 117) {
+ sayLines(2, 1496);
+ return 1;
+ }
+ break;
+ case 84:
+ if (itemSrc == 0x58) {
+ replaceItemFromInventory(88, 90);
+ characterTalk(1090);
+ return 1;
+ } else if (itemSrc == 117) {
+ characterTalk(1494);
+ return 1;
+ }
+ break;
+ case 88:
+ if (itemSrc == 82) {
+ deleteItemFromInventory(82);
+ createMouseItem(89);
+ rearrangeInventory();
+ characterTalk(1428);
+ return 1;
+ } else if (itemSrc == 0x54) {
+ createMouseItem(90);
+ characterTalk(1090);
+ return 1;
+ } else if (itemSrc == 102) {
+ deleteItemFromInventory(102);
+ createMouseItem(90);
+ rearrangeInventory();
+ characterTalk(1090);
+ return 1;
+ }
+ break;
+ case 89:
+ if (itemSrc == 117) {
+ sayLines(2, 1496);
+ return 1;
+ }
+ break;
+ case 90:
+ if (itemSrc == 117) {
+ sayLines(2, 1494);
+ return 1;
+ }
+ break;
+ case 91:
+ if (itemSrc == 0x28) {
+ characterTalk(1230);
+ return 1;
+ } else if (itemSrc == 54) {
+ createMouseItem(92);
+ return 1;
+ }
+ break;
+ case 92:
+ if (itemSrc == 0x28 || itemSrc == 54) {
+ characterTalk(1220);
+ return 1;
+ }
+ break;
+ case 94:
+ if (itemSrc == 26) {
+ characterTalk(1316);
+ return 1;
+ } else if (itemSrc == 0x34) {
+ characterTalk(1282);
+ return 1;
+ } else if (itemSrc == 64) {
+ characterTalk(1318);
+ return 1;
+ }
+ break;
+ case 95:
+ if (itemSrc == 26) {
+ characterTalk(1320);
+ return 1;
+ } else if (itemSrc == 0x40) {
+ characterTalk(1318);
+ return 1;
+ } else if (itemSrc == 115) {
+ characterTalk(1284);
+ replaceItemFromInventory(115, 116);
+ createMouseItem(93);
+ return 1;
+ }
+ break;
+ case 96:
+ if (itemSrc == 0x34) {
+ characterTalk(1234);
+ return 1;
+ } else if (itemSrc == 71) {
+ sayLines(2, 1212);
+ return 1;
+ }
+ break;
+ case 97:
+ if (itemSrc == 15) {
+ characterTalk(1492);
+ return 1;
+ } else if (itemSrc == 0x2f) {
+ characterTalk(1488);
+ return 1;
+ } else if (itemSrc == 88) {
+ sayLines(2, 1478);
+ return 1;
+ }
+ break;
+ case 100:
+ if (itemSrc == 117) {
+ characterTalk(1490);
+ return 1;
+ }
+ break;
+ case 102:
+ if (itemSrc == -1) {
+ characterTalk(1102);
+ return 1;
+ } else if (itemSrc == 84) {
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ rearrangeInventory();
+ characterTalk(1418);
+ return 1;
+ } else if (itemSrc == 88) {
+ deleteItemFromInventory(88);
+ createMouseItem(90);
+ rearrangeInventory();
+ characterTalk(1090);
+ return 1;
+ } else if (itemSrc == 117) {
+ characterTalk(1494);
+ return 1;
+ } else {
+ characterTalk(1426);
+ return 1;
+ }
+ break;
+ case 106:
+ if (itemSrc == 13) {
+ characterTalk(1308);
+ return 1;
+ }
+ break;
+ case 107:
+ if (itemSrc == 19) {
+ sayLines(2, 1300);
+ deleteItemFromInventory(19);
+ createMouseItem(111);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x15) {
+ characterTalk(1296);
+ deleteItemFromInventory(21);
+ createMouseItem(109);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x23) {
+ characterTalk(1314);
+ return 1;
+ } else if (itemSrc == 69) {
+ characterTalk(1296);
+ deleteItemFromInventory(69);
+ createMouseItem(108);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 108:
+ if (itemSrc == 19) {
+ sayLines(2, 1300);
+ deleteItemFromInventory(19);
+ createMouseItem(112);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x15) {
+ characterTalk(1298);
+ deleteItemFromInventory(21);
+ createMouseItem(110);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 35) {
+ characterTalk(1314);
+ return 1;
+ }
+ break;
+ case 109:
+ if (itemSrc == 19) {
+ sayLines(2, 1300);
+ deleteItemFromInventory(19);
+ createMouseItem(113);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x23) {
+ characterTalk(1314);
+ return 1;
+ } else if (itemSrc == 69) {
+ characterTalk(1298);
+ deleteItemFromInventory(69);
+ createMouseItem(110);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 110:
+ if (itemSrc == 0x13) {
+ sayLines(2, 1300);
+ deleteItemFromInventory(19);
+ createMouseItem(114);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 35) {
+ characterTalk(1314);
+ return 1;
+ }
+ break;
+ case 111:
+ if (itemSrc == 21) {
+ characterTalk(1296);
+ deleteItemFromInventory(21);
+ createMouseItem(113);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 0x23) {
+ characterTalk(1314);
+ return 1;
+ } else if (itemSrc == 69) {
+ characterTalk(1296);
+ deleteItemFromInventory(69);
+ createMouseItem(112);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 112:
+ if (itemSrc == 0x15) {
+ characterTalk(1298);
+ deleteItemFromInventory(21);
+ createMouseItem(114);
+ rearrangeInventory();
+ return 1;
+ } else if (itemSrc == 35) {
+ characterTalk(1314);
+ return 1;
+ }
+ break;
+ case 113:
+ if (itemSrc == 0x23) {
+ characterTalk(1314);
+ return 1;
+ } else if (itemSrc == 69) {
+ characterTalk(1298);
+ deleteItemFromInventory(69);
+ createMouseItem(114);
+ rearrangeInventory();
+ return 1;
+ }
+ break;
+ case 114:
+ if (itemSrc == 35) {
+ characterTalk(1314);
+ return 1;
+ }
+ break;
+ case 115:
+ if (itemSrc == 95) {
+ replaceItemFromInventory(95, 93);
+ createMouseItem(116);
+ return 1;
+ }
+ break;
+ case 117:
+ if (itemSrc == 90 || itemSrc == 33) {
+ characterTalk(1490);
+ } else if (itemSrc == 102 || itemSrc == 84) {
+ characterTalk(1494);
+ } else if (itemSrc == 0x59 || itemSrc == 0x52) {
+ characterTalk(1496);
+ }
+ }
+ return 0;
+}
+int32 ToonEngine::handleInventoryOnDrew(int32 itemId) {
+ switch (itemId) {
+ case 1:
+ sayLines(1, 1232);
+ return 1;
+ case 2:
+ sayLines(2, 1202);
+ return 1;
+ case 7:
+ if (_gameState->_currentScene == 32) {
+ runEventScript(_mouseX, _mouseY, 2, 107, 0);
+ } else if (_gameState->_currentScene < 37) {
+ sayLines(2, 1258);
+ } else {
+ sayLines(2, 1462);
+ }
+ return 1;
+ case 8:
+ sayLines(2, 1328);
+ return 1;
+ case 0xc:
+ sayLines(1, 1266);
+ return 1;
+ case 0xd:
+ sayLines(1, 1206);
+ return 1;
+ case 16:
+ sayLines(1, 1438);
+ return 1;
+ case 0x12:
+ if (_gameState->_currentScene == 30) {
+ runEventScript(_mouseX, _mouseY, 2, 106, 0);
+ _gameState->_mouseState = -1;
+ } else {
+ sayLines(2, 1200);
+ }
+ return 1;
+ case 0x14:
+ sayLines(1, 1216);
+ return 1;
+ case 22:
+ if (_gameState->_currentScene != 39 && _gameState->_currentScene != 50 && _gameState->_currentScene != 49) {
+ if (_gameState->_currentScene < 37) {
+ sayLines(1, 1256);
+ } else {
+ sayLines(1, 1456);
+ }
+ } else {
+ runEventScript(_mouseX, _mouseY, 2, 100 , 0);
+ }
+ return 1;
+ case 0x18:
+ sayLines(1, 1216);
+ return 1;
+ case 0x23:
+ sayLines(1, 1210);
+ return 1;
+ case 0x31:
+ sayLines(1, 1262);
+ return 1;
+ case 50:
+ if (_gameState->_currentScene == 37) {
+ runEventScript(_mouseX, _mouseY, 2, 103, 0);
+ return 1;
+ };
+ break;
+ case 0x36:
+ if (_gameState->_currentScene == 46) {
+ runEventScript(_mouseX, _mouseY, 2, 102, 0);
+ } else {
+ sayLines(1, 1224);
+ }
+ return 1;
+ case 0x37:
+ sayLines(1, 1408);
+ return 1;
+ case 0x20:
+ sayLines(1, 1254);
+ return 1;
+ case 0x21:
+ sayLines(1, 1268);
+ return 1;
+ case 0x22:
+ if (_gameState->_currentScene == 52) {
+ runEventScript(_mouseX, _mouseY, 2, 104, 0);
+ return 1;
+ } else {
+ _gameState->_mouseHidden = true;
+ _drew->setFacing(4);
+ sayLines(1, 1465);
+ sayLines(1, randRange(0, 1) + 1468);
+ createMouseItem(33);
+ _gameState->_mouseHidden = false;
+ return 1;
+ }
+ break;
+ case 31:
+ sayLines(1, 1436);
+ return 1;
+ case 0x1a:
+ sayLines(1, 1216);
+ return 1;
+ case 0x39:
+ sayLines(1, 1270);
+ return 1;
+ case 0x3a:
+ sayLines(1, 1444);
+ return 1;
+ case 0x3b:
+ sayLines(1, 1272);
+ return 1;
+ case 0x3f:
+ if (_gameState->_currentScene != 10 && _gameState->_currentScene != 30 && _gameState->_currentScene != 22) {
+ sayLines(1, 1274);
+ } else {
+ runEventScript(_mouseX, _mouseY, 2, 109, 0);
+ }
+ return 1;
+ case 0x41:
+ sayLines(1, 1232);
+ return 1;
+
+ case 0x4b:
+ if (_gameState->_currentScene != 53) {
+ _gameState->_mouseHidden = true;
+ _drew->setFacing(4);
+ sayLines(1, 1437);
+ sayLines(2, 1440);
+ _gameState->_mouseHidden = false;
+ } else {
+ runEventScript(_mouseX, _mouseY, 2 , 101, 0);
+ }
+ return 1;
+ case 79:
+ sayLines(1, 1242);
+ return 1;
+ case 0x4c:
+ sayLines(1, 1232);
+ return 1;
+ case 71:
+ sayLines(1, 1250);
+ return 1;
+ case 0x43:
+ sayLines(1, 1216);
+ return 1;
+ case 0x60:
+ sayLines(2, 1236);
+ return 1;
+ case 99:
+ if (_gameState->_currentScene == 43) {
+ runEventScript(_mouseX, _mouseY, 2, 105, 0);
+ }
+ _gameState->_mouseState = -1;
+ setCursor(0, false, 0, 0);
+ sayLines(1, 1555);
+ return 1;
+ case 0x5a:
+ sayLines(1, 1432);
+ return 1;
+ case 0x58:
+ sayLines(1, 1432);
+ return 1;
+ case 0x65:
+ if (_gameState->_currentScene == 52) {
+ runEventScript(_mouseX, _mouseY, 2, 104, 0);
+ } else {
+ _gameState->_mouseHidden = true;
+ _drew->setFacing(4);
+ sayLines(1, 1464);
+ sayLines(1, 1468 + randRange(0, 1));
+ createMouseItem(100);
+ _gameState->_mouseHidden = false;
+ }
+ return 1;
+ case 0x74:
+ sayLines(1, 1286);
+ return 1;
+ case 0x75:
+ sayLines(1, 1482);
+ return 1;
+ case 118:
+ sayLines(2, 1500);
+ return 1;
+ case 115:
+ sayLines(1, 1216);
+ return 1;
+ case 0x67:
+ if (_gameState->_currentScene == 52 || _gameState->_currentScene == 53) {
+ runEventScript(_mouseX, _mouseY, 2, 108, 0);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void ToonEngine::deleteItemFromInventory(int32 item) {
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ if (_gameState->_inventory[i] == item) {
+ _gameState->_inventory[i] = 0;
+ rearrangeInventory();
+ return;
+ }
+ }
+}
+
+void ToonEngine::replaceItemFromInventory(int32 item, int32 newitem) {
+ for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
+ if (_gameState->_inventory[i] == item) {
+ _gameState->_inventory[i] = newitem;
+ return;
+ }
+ }
+}
+
+int32 ToonEngine::pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait) {
+ int32 nextTicks = getTickLength() * tickToWait + getSceneAnimationScript(animScriptId)->_lastTimer;
+ if (nextTicks < getOldMilli()) {
+ getSceneAnimationScript(animScriptId)->_lastTimer = getOldMilli() + getTickLength() * tickToWait;
+ } else {
+ getSceneAnimationScript(animScriptId)->_lastTimer = nextTicks;
+ }
+ return nextTicks;
+}
+
+Common::String ToonEngine::createRoomFilename(Common::String name) {
+ Common::String file = Common::String::printf("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str());
+ return file;
+}
+
+void ToonEngine::createShadowLUT() {
+ // here we create the redirection table that will be used to draw shadows
+ // for each color of the palette we find the closest color in the palette that could be used for shadowed color.
+
+ // In the original program, the scale factor is 0.77f
+ // we will use 77 / 100 here.
+
+ if (!_shadowLUT) {
+ _shadowLUT = new uint8[256];
+ }
+
+ uint32 scaleNum = 77;
+ uint32 scaleDenom = 100;
+
+ for (int32 i = 0; i < 255; i++) {
+
+ // goal color
+ uint32 destR = _finalPalette[i*3+0] * scaleNum / scaleDenom;
+ uint32 destG = _finalPalette[i*3+1] * scaleNum / scaleDenom;
+ uint32 destB = _finalPalette[i*3+2] * scaleNum / scaleDenom;
+
+ // search only in the "picture palette" which is in colors 1-128 and 200-255
+ int32 colorDist = 0xffffff;
+ int32 foundColor = 0;
+
+ for (int32 c = 1; c < 129; c++) {
+
+ int32 diffR = _finalPalette[c*3+0] - destR;
+ int32 diffG = _finalPalette[c*3+1] - destG;
+ int32 diffB = _finalPalette[c*3+2] - destB;
+
+ if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
+ colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
+ foundColor = c;
+ }
+ }
+
+ for (int32 c = 200; c < 256; c++) {
+
+ int32 diffR = _finalPalette[c*3+0] - destR;
+ int32 diffG = _finalPalette[c*3+1] - destG;
+ int32 diffB = _finalPalette[c*3+2] - destB;
+
+ if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
+ colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
+ foundColor = c;
+ }
+ }
+
+ _shadowLUT[i] = foundColor;
+
+ }
+}
+
+bool ToonEngine::loadToonDat() {
+ Common::File in;
+ char buf[256];
+ int majVer, minVer;
+
+ in.open("toon.dat");
+
+ if (!in.isOpen()) {
+ Common::String errorMessage = "You're missing the 'toon.dat' file. Get it from the ScummVM website";
+ GUIErrorMessage(errorMessage);
+ warning("%s", errorMessage.c_str());
+ return false;
+ }
+
+ // Read header
+ in.read(buf, 4);
+ buf[4] = '\0';
+
+ if (strcmp(buf, "TOON")) {
+ Common::String errorMessage = "File 'toon.dat' is corrupt. Get it from the ScummVM website";
+ GUIErrorMessage(errorMessage);
+ warning("%s", errorMessage.c_str());
+ return false;
+ }
+
+ majVer = in.readByte();
+ minVer = in.readByte();
+
+ if ((majVer != TOON_DAT_VER_MAJ) || (minVer != TOON_DAT_VER_MIN)) {
+ snprintf(buf, 256, "File 'toon.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TOON_DAT_VER_MAJ, TOON_DAT_VER_MIN, majVer, minVer);
+ GUIErrorMessage(buf);
+ warning("%s", buf);
+
+ return false;
+ }
+
+ _numVariant = in.readUint16BE();
+
+ _locationDirNotVisited = loadTextsVariante(in);
+ _locationDirVisited = loadTextsVariante(in);
+ _specialInfoLine = loadTextsVariante(in);
+
+ return true;
+}
+
+char **ToonEngine::loadTextsVariante(Common::File &in) {
+ int numTexts;
+ int entryLen;
+ int len;
+ char **res = 0;
+ char *pos = 0;
+
+ for (int varnt = 0; varnt < _numVariant; varnt++) {
+ numTexts = in.readUint16BE();
+ entryLen = in.readUint16BE();
+ pos = (char *)malloc(entryLen);
+ if (varnt == _gameVariant) {
+ res = (char **)malloc(sizeof(char *) * numTexts);
+ res[0] = pos;
+ in.read(res[0], entryLen);
+ res[0] += DATAALIGNMENT;
+ } else {
+ in.read(pos, entryLen);
+ }
+
+ pos += DATAALIGNMENT;
+
+ for (int i = 1; i < numTexts; i++) {
+ pos -= 2;
+
+ len = READ_BE_UINT16(pos);
+ pos += 2 + len;
+
+ if (varnt == _gameVariant)
+ res[i] = pos;
+ }
+ }
+
+ return res;
+}
+
+void ToonEngine::makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2) {
+ _currentMask->drawLineOnMask(x, y, x2, y2, false);
+}
+
+void ToonEngine::makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2) {
+ _currentMask->drawLineOnMask(x, y, x2, y2, true);
+}
+
+void ToonEngine::playRoomMusic() {
+ if(_gameState->_inConversation) {
+ const char* music = getSpecialConversationMusic(_gameState->_currentConversationId);
+ if (music) {
+ _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, music);
+ return;
+ }
+ }
+
+ _audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music);
+}
+
+void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) {
+ stream->writeByte(_active);
+ stream->writeSint32BE(_id);
+
+ if (!_active)
+ return;
+
+ if (_animInstance) {
+ stream->writeByte(1);
+ _animInstance->save(stream);
+ } else {
+ stream->writeByte(0);
+ }
+
+ if (!_animation) {
+ stream->writeByte(0);
+ } else {
+ stream->writeByte(strlen(_animation->_name) + 1);
+ stream->write(_animation->_name, strlen(_animation->_name) + 1);
+ }
+}
+void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) {
+
+ _active = stream->readByte();
+ _id = stream->readSint32BE();
+
+
+ if (!_active)
+ return;
+
+ if (stream->readByte() == 1) {
+ _animInstance = vm->getAnimationManager()->createNewInstance(kAnimationScene);
+ _animInstance->load(stream);
+ vm->getAnimationManager()->addInstance(_animInstance);
+ }
+
+ // load animation if any
+ char animationName[256];
+ *animationName = 0;
+ int8 strSize = stream->readByte();
+ if (!strSize) {
+ _animation = 0;
+ if (_animInstance)
+ _animInstance->setAnimation(0);
+ } else {
+ stream->read(animationName, strSize);
+ animationName[strSize] = 0;
+
+ _animation = new Animation(vm);
+ _animation->loadAnimation(animationName);
+
+ if (_animInstance)
+ _animInstance->setAnimation(_animation, false);
+
+ printf("load animation instance %d / %s / visible %d \n", _id, _animation->_name, _animInstance->getVisible());
+ }
+}
+
+} // End of namespace Toon
diff --git a/engines/toon/toon.h b/engines/toon/toon.h
new file mode 100644
index 0000000000..30aa344517
--- /dev/null
+++ b/engines/toon/toon.h
@@ -0,0 +1,388 @@
+/* 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 TOON_TOON_H
+#define TOON_TOON_H
+
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+#include "graphics/surface.h"
+#include "common/random.h"
+#include "toon/resource.h"
+#include "toon/script.h"
+#include "toon/script_func.h"
+#include "toon/state.h"
+#include "toon/picture.h"
+#include "toon/anim.h"
+#include "toon/movie.h"
+#include "toon/font.h"
+#include "toon/text.h"
+#include "toon/audio.h"
+
+#define TOON_DAT_VER_MAJ 0 // 1 byte
+#define TOON_DAT_VER_MIN 3 // 1 byte
+#define TOON_SAVEGAME_VERSION 4
+#define DATAALIGNMENT 4
+
+namespace Toon {
+enum ToonGameType {
+ GType_TOON = 1
+};
+
+enum ToonDebugChannels {
+ kDebugAnim = 1 << 0,
+ kDebugCharacter = 1 << 1,
+ kDebugAudio = 1 << 2,
+ kDebugHotspot = 1 << 3,
+ kDebugFont = 1 << 4,
+ kDebugPath = 1 << 5,
+ kDebugMovie = 1 << 6,
+ kDebugPicture = 1 << 7,
+ kDebugResource = 1 << 8,
+ kDebugState = 1 << 9,
+ kDebugTools = 1 << 10,
+ kDebugText = 1 << 11
+};
+
+class Picture;
+class Movie;
+class Hotspots;
+class Character;
+class CharacterDrew;
+class CharacterFlux;
+class FontRenderer;
+class TextResource;
+class AudioManager;
+class PathFinding;
+
+class ToonEngine : public Engine {
+public:
+ ToonEngine(OSystem *syst, const ADGameDescription *gameDescription);
+ ~ToonEngine();
+
+ const ADGameDescription *_gameDescription;
+ Common::Language _language;
+ byte _numVariant;
+ byte _gameVariant;
+ char **_locationDirNotVisited;
+ char **_locationDirVisited;
+ char **_specialInfoLine;
+
+ Common::Error run();
+ bool showMainmenu(bool &loadedGame);
+ void init();
+ bool loadToonDat();
+ char **loadTextsVariante(Common::File &in);
+ void setPaletteEntries(uint8 *palette, int32 offset, int32 num);
+ void fixPaletteEntries(uint8 *palette, int num);
+ void flushPalette();
+ void parseInput();
+ void initChapter();
+ void initFonts();
+ void loadScene(int32 SceneId, bool forGameLoad = false);
+ void exitScene();
+ void loadCursor();
+ void setCursor(int32 type, bool inventory = false, int32 offsetX = 0, int offsetY = 0);
+ void loadAdditionalPalette(Common::String fileName, int32 mode);
+ void setupGeneralPalette();
+ void render();
+ void update(int32 timeIncrement);
+ void doFrame();
+ void updateAnimationSceneScripts(int32 timeElapsed);
+ void updateCharacters(int32 timeElapsed);
+ void setSceneAnimationScriptUpdate(bool enable);
+ bool isUpdatingSceneAnimation();
+ int32 getCurrentUpdatingSceneAnimation();
+ int32 randRange(int32 minStart, int32 maxStart);
+ void selectHotspot();
+ void clickEvent();
+ int32 runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId);
+ void flipScreens();
+ void drawInfoLine();
+ void drawConversationLine();
+ const char *getLocationString(int32 locationId, bool alreadyVisited);
+ int32 getScaleAtPoint(int32 x, int32 y);
+ int32 getZAtPoint(int32 x, int32 y);
+ int32 getLayerAtPoint(int32 x, int32 y);
+ int32 characterTalk(int32 dialogid, bool blocking = true);
+ int32 simpleCharacterTalk(int32 dialogid);
+ void sayLines(int numLines, int dialogId);
+ void haveAConversation(int32 convId);
+ void processConversationClick(Conversation *conv, int32 status);
+ int32 runConversationCommand(int16 **command);
+ void prepareConversations();
+ void drawConversationIcons();
+ void simpleUpdate();
+ int32 waitTicks(int32 numTicks, bool breakOnMouseClick);
+ void copyToVirtualScreen(bool updateScreen = true);
+ void getMouseEvent();
+ int32 showInventory();
+ void drawSack();
+ void addItemToInventory(int32 item);
+ void deleteItemFromInventory(int32 item);
+ void replaceItemFromInventory(int32 item, int32 destItem);
+ void rearrangeInventory();
+ void createMouseItem(int32 item);
+ void deleteMouseItem();
+ void showCutaway(Common::String cutawayPicture);
+ void hideCutaway();
+ void drawPalette();
+ void newGame();
+ void playSoundWrong();
+ void playSFX(int32 id, int32 volume);
+ void storeRifFlags(int32 location);
+ void restoreRifFlags(int32 location);
+ void getTextPosition(int32 characterId, int32 *retX, int32 *retY);
+ int32 getConversationFlag(int32 locationId, int32 param);
+ int32 getSpecialInventoryItem(int32 item);
+ Character *getCharacterById(int32 charId);
+ Common::String getSavegameName(int nr);
+ bool loadGame(int32 slot);
+ bool saveGame(int32 slot);
+ void fadeIn(int32 numFrames) ;
+ void fadeOut(int32 numFrames) ;
+ void initCharacter(int32 characterId, int32 animScriptId, int32 animToPlayId, int32 sceneAnimationId);
+ int32 handleInventoryOnFlux(int32 itemId);
+ int32 handleInventoryOnInventory(int32 itemDest, int32 itemSrc);
+ int32 handleInventoryOnDrew(int32 itemId);
+ int32 pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait);
+ void updateTimer(int32 timeIncrement);
+ Common::String createRoomFilename(Common::String name);
+ void createShadowLUT();
+ void playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker);
+ void updateScrolling(bool force, int32 timeIncrement);
+ void enableTimer(int32 timerId);
+ void setTimer(int32 timerId, int32 timerWait);
+ void disableTimer(int32 timerId);
+ void updateTimers();
+ void makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2);
+ void makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2);
+ void renderInventory();
+ void viewInventoryItem(Common::String str, int32 lineId, int32 itemDest);
+ void storePalette();
+ void restorePalette();
+ const char *getSpecialConversationMusic(int32 locationId);
+ void playRoomMusic();
+ void waitForScriptStep();
+
+ Resources *resources() {
+ return _resources;
+ }
+
+ State *state() {
+ return _gameState;
+ }
+
+ Graphics::Surface &getMainSurface() {
+ return *_mainSurface;
+ }
+
+ Picture *getMask() {
+ return _currentMask;
+ }
+
+ Picture *getPicture() {
+ return _currentPicture;
+ }
+
+ AnimationManager *getAnimationManager() {
+ return _animationManager;
+ }
+
+ Movie *getMoviePlayer() {
+ return _moviePlayer;
+ }
+
+ SceneAnimation *getSceneAnimation(int32 id) {
+ return &_sceneAnimations[id];
+ }
+
+ SceneAnimationScript *getSceneAnimationScript(int32 id) {
+ return &_sceneAnimationScripts[id];
+ }
+
+ EMCInterpreter *getScript() {
+ return _script;
+ }
+
+ Hotspots *getHotspots() {
+ return _hotspots;
+ }
+
+ Character *getCharacter(int32 charId) {
+ return _characters[charId];
+ }
+
+ uint8 *getShadowLUT() {
+ return _shadowLUT;
+ }
+
+ int32 getCurrentLineToSay() {
+ return _currentTextLineId;
+ }
+
+ CharacterDrew *getDrew() {
+ return (CharacterDrew *)_drew;
+ }
+
+ CharacterFlux *getFlux() {
+ return (CharacterFlux *)_flux;
+ }
+
+ int32 getTickLength() {
+ return _tickLength;
+ }
+
+ int32 getOldMilli() {
+ return _oldTimer2;
+ }
+
+ OSystem *getSystem() {
+ return _system;
+ }
+
+ AudioManager *getAudioManager() {
+ return _audioManager;
+ }
+
+ int32 getScriptRegionNested() {
+ return _currentScriptRegion;
+ }
+
+ int32 getMouseX() {
+ return _mouseX;
+ }
+
+ int32 getMouseY() {
+ return _mouseY;
+ }
+
+ PathFinding *getPathFinding() {
+ return _pathFinding;
+ }
+
+ Common::WriteStream *getSaveBufferStream() {
+ return _saveBufferStream;
+ }
+
+ bool shouldQuitGame() const {
+ return _shouldQuit;
+ }
+
+protected:
+ OSystem *_system;
+ int32 _tickLength;
+ Resources *_resources;
+ TextResource *_genericTexts;
+ TextResource *_roomTexts;
+ State *_gameState;
+ uint8 *_finalPalette;
+ uint8 *_backupPalette;
+ uint8 *_additionalPalette1;
+ uint8 *_additionalPalette2;
+ uint8 *_cutawayPalette;
+ uint8 *_universalPalette;
+ uint8 *_fluxPalette;
+ uint8 *_roomScaleData;
+ uint8 *_shadowLUT;
+
+ Picture *_currentPicture;
+ Picture *_currentMask;
+ Picture *_currentCutaway;
+ Picture *_inventoryPicture;
+ PathFinding *_pathFinding;
+
+ EMCInterpreter *_script;
+ EMCData _scriptData;
+ EMCState _scriptState[4];
+ int32 _currentScriptRegion; // script region ( nested script run )
+
+ ScriptFunc *_script_func;
+
+ SceneAnimation _sceneAnimations[64];
+ SceneAnimationScript _sceneAnimationScripts[64];
+ int32 _lastProcessedSceneScript;
+ bool _animationSceneScriptRunFlag;
+ bool _updatingSceneScriptRunFlag;
+
+ Graphics::Surface *_mainSurface;
+
+ AnimationInstance *_cursorAnimationInstance;
+ Animation *_cursorAnimation;
+ Animation *_dialogIcons;
+ Animation *_inventoryIcons;
+ Animation *_inventoryIconSlots;
+ int32 _cursorOffsetX;
+ int32 _cursorOffsetY;
+
+ char *_currentTextLine;
+ int32 _currentTextLineId;
+ int32 _currentTextLineX;
+ int32 _currentTextLineY;
+ int32 _currentTextLineCharacterId;
+
+ int32 _oldScrollValue;
+
+ AnimationManager *_animationManager;
+
+ Character *_characters[32];
+ Character *_drew;
+ Character *_flux;
+
+ Hotspots *_hotspots;
+ int32 _currentHotspotItem;
+
+ bool _shouldQuit;
+ int32 _scriptStep;
+
+ int32 _mouseX;
+ int32 _mouseY;
+ int32 _mouseButton;
+ int32 _lastMouseButton;
+
+ int32 _oldTimer;
+ int32 _oldTimer2;
+
+ Movie *_moviePlayer;
+
+ Common::RandomSource _rnd;
+
+ FontRenderer *_fontRenderer;
+ Animation *_fontToon;
+ Animation *_fontEZ;
+
+ AudioManager *_audioManager;
+
+ Common::MemoryWriteStreamDynamic *_saveBufferStream;
+
+ int16 *_conversationData;
+
+ bool _firstFrame;
+ bool _isDemo;
+};
+
+} // End of namespace Toon
+
+#endif
diff --git a/engines/touche/midi.cpp b/engines/touche/midi.cpp
index 439d3b9ac2..ee7602abf7 100644
--- a/engines/touche/midi.cpp
+++ b/engines/touche/midi.cpp
@@ -101,6 +101,11 @@ int MidiPlayer::open() {
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
_driver->setTimerCallback(this, &timerCallback);
+
+ if (_nativeMT32)
+ _driver->sendMT32Reset();
+ else
+ _driver->sendGMReset();
}
return ret;
}